From 3e4a0e383110f275736595b7175f5612debdc551 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Mon, 23 Feb 2026 14:14:58 +0100 Subject: [PATCH 01/22] added enum with deletion modes --- projects/emfular/src/lib/utils/deletion-mode.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/emfular/src/lib/utils/deletion-mode.ts diff --git a/projects/emfular/src/lib/utils/deletion-mode.ts b/projects/emfular/src/lib/utils/deletion-mode.ts new file mode 100644 index 0000000..baec52f --- /dev/null +++ b/projects/emfular/src/lib/utils/deletion-mode.ts @@ -0,0 +1,4 @@ +export enum DeletionMode { + CASCADE = "CASCADE", // delete all classes that become inconsistent after deletion + RELAXED = "RELAXED" // delete only the specified class, even if it leaves the model in an inconsistent state +} \ No newline at end of file From 3a01d50959a70dc51cf8c20ab716ecc95f35d6da Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 24 Feb 2026 00:11:26 +0100 Subject: [PATCH 02/22] added isRequired flag to ReContainer --- .../lib/referencing/referencable/container/re-container.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index 3e42fb4..090fbde 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -7,10 +7,12 @@ export abstract class ReContainer< > { readonly _parent: P; readonly referenceName: string; + readonly isRequired: boolean; - constructor(parent: P, referenceName: string) { + constructor(parent: P, referenceName: string, isRequired: boolean = false) { this._parent = parent; this.referenceName = referenceName; + this.isRequired = isRequired; } abstract get(): T[] | T | undefined; From ce38944e7f8ba6bde51a27f69ce73bd157e7e45c Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 24 Feb 2026 01:31:42 +0100 Subject: [PATCH 03/22] remove isRequired flag from ReContainer --- .../src/lib/referencing/referencable/container/re-container.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index 090fbde..f3679f3 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -7,12 +7,10 @@ export abstract class ReContainer< > { readonly _parent: P; readonly referenceName: string; - readonly isRequired: boolean; constructor(parent: P, referenceName: string, isRequired: boolean = false) { this._parent = parent; this.referenceName = referenceName; - this.isRequired = isRequired; } abstract get(): T[] | T | undefined; From 89672e30e1c9c71e92964efa7af9718392df10e1 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 24 Feb 2026 01:40:23 +0100 Subject: [PATCH 04/22] added mode arguments to destruct, destructAllFromChangingList and delete --- .../referencable/container/link/re-link-list-container.ts | 5 +++-- .../referencable/container/link/re-link-single-container.ts | 5 +++-- .../lib/referencing/referencable/container/re-container.ts | 3 ++- .../referencable/container/tree/re-tree-list-container.ts | 5 +++-- .../referencable/container/tree/re-tree-single-container.ts | 5 +++-- .../src/lib/referencing/referencable/referenceable.ts | 5 +++-- projects/emfular/src/lib/utils/list-updater.ts | 5 +++-- 7 files changed, 20 insertions(+), 13 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts index 65290d4..cf27268 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts @@ -3,6 +3,7 @@ import {Ref} from "../../../ref/ref"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {ReLinkContainer} from "./re-link-container"; import {ListUpdater} from "../../../../utils/list-updater"; +import { DeletionMode } from "../../../../utils/deletion-mode"; export class ReLinkListContainer< T extends Referencable, @@ -45,8 +46,8 @@ export class ReLinkListContainer< return res; //todo behaviour of flag different to add?? } - override delete() { - ListUpdater.destructAllFromChangingList(this._instance) + override delete(mode: DeletionMode) { + ListUpdater.destructAllFromChangingList(this._instance, mode) } override removeFromInverse(item: T): boolean { diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts index 1f672de..cd3f964 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts @@ -2,6 +2,7 @@ import {Referencable} from "../../referenceable"; import {Ref} from "../../../ref/ref"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {ReLinkContainer} from "./re-link-container"; +import { DeletionMode } from "../../../../utils/deletion-mode"; export class ReLinkSingleContainer< T extends Referencable, @@ -49,8 +50,8 @@ export class ReLinkSingleContainer< } } - override delete() { - this._instance?.destruct() + override delete(mode: DeletionMode) { + this._instance?.destruct(mode) } removeFromInverse(item: T): boolean { diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index f3679f3..4cd35b8 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -1,5 +1,6 @@ import {Referencable} from "../referenceable"; import {SerializationContext} from "../../../serialization/serialization-context"; +import { DeletionMode } from "../../../utils/deletion-mode"; export abstract class ReContainer< T extends Referencable, @@ -20,7 +21,7 @@ export abstract class ReContainer< abstract remove(item: T): boolean; //called to destruct all elements in the container (e.g. when destroying a parent - abstract delete(): void + abstract delete(mode: DeletionMode): void abstract toJson(ctx: SerializationContext): any } diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts index af7346b..39a17c7 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts @@ -5,6 +5,7 @@ import {JsonOf} from "../../../../serialization/json-deserializable"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {ReTreeChildrenContainer} from "./re-tree-children-container"; import {ListUpdater} from "../../../../utils/list-updater"; +import { DeletionMode } from "../../../../utils/deletion-mode"; export class ReTreeListContainer> extends ReTreeChildrenContainer { @@ -53,8 +54,8 @@ export class ReTreeListContainer> return false; } - override delete() { - ListUpdater.destructAllFromChangingList(this._instance) + override delete(mode: DeletionMode) { + ListUpdater.destructAllFromChangingList(this._instance, mode) } //creates one child level plus calls next createChildren diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts index 79af899..3a64dbb 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts @@ -4,6 +4,7 @@ import {Deserializer} from "../../../../serialization/deserializer"; import {JsonOf} from "../../../../serialization/json-deserializable"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {ReTreeChildrenContainer} from "./re-tree-children-container"; +import { DeletionMode } from "../../../../utils/deletion-mode"; export class ReTreeSingleContainer> extends ReTreeChildrenContainer { @@ -45,8 +46,8 @@ export class ReTreeSingleContainer> return false; } - override delete() { - this._instance?.destruct() + override delete(mode: DeletionMode) { + this._instance?.destruct(mode) } fromJson(formerPrefix: string, context: Deserializer, json: any) { diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index 373b564..a7ec78f 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -10,6 +10,7 @@ import {RefHandler} from "../ref/ref-handler"; import {ReTreeChildrenContainer} from "./container/tree/re-tree-children-container"; import {ReLinkContainer} from "./container/link/re-link-container"; import {ModelRegistry} from "../../binding/model-registry"; +import { DeletionMode } from "../../utils/deletion-mode"; /** base class for CORE models. * @@ -58,13 +59,13 @@ export abstract class Referencable< } } - destruct() { + destruct(mode: DeletionMode) { this.$parent?.remove(this) this.$otherReferences.forEach(refContainer => { refContainer.removeFromInverse(this) }) this.$treeChildren.forEach(child => { - child.delete() + child.delete(mode) }) } diff --git a/projects/emfular/src/lib/utils/list-updater.ts b/projects/emfular/src/lib/utils/list-updater.ts index dd97126..c2448e2 100644 --- a/projects/emfular/src/lib/utils/list-updater.ts +++ b/projects/emfular/src/lib/utils/list-updater.ts @@ -1,4 +1,5 @@ import {Referencable} from "../referencing/referencable/referenceable"; +import { DeletionMode } from "./deletion-mode"; export class ListUpdater { @@ -25,9 +26,9 @@ export class ListUpdater { } } - static destructAllFromChangingList>(list: T[]) { + static destructAllFromChangingList>(list: T[], mode: DeletionMode) { while(list?.length > 0){ - list[0].destruct() + list[0].destruct(mode) } } From 1fd59139c280cd10fefd1c4be39763def2b58eb0 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 24 Feb 2026 02:45:40 +0100 Subject: [PATCH 05/22] added mode arguments to remove() calls --- .../referencable/container/link/re-link-container.ts | 3 ++- .../container/link/re-link-list-container.ts | 8 ++++---- .../container/link/re-link-single-container.ts | 10 +++++----- .../referencing/referencable/container/re-container.ts | 2 +- .../container/tree/re-tree-list-container.ts | 2 +- .../container/tree/re-tree-parent-container.ts | 7 ++++--- .../src/lib/referencing/referencable/referenceable.ts | 10 +++++----- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts index dfd7f8b..93d8559 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts @@ -3,6 +3,7 @@ import { ReContainer } from "../re-container"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {Ref} from "../../../ref/ref"; import {Deserializer} from "../../../../serialization/deserializer"; +import { DeletionMode } from "../../../../utils/deletion-mode"; export abstract class ReLinkContainer< T extends Referencable, @@ -17,7 +18,7 @@ export abstract class ReLinkContainer< this._parent.$otherReferences.push(this) } - abstract removeFromInverse(item: T): boolean; + abstract removeFromInverse(item: T, mode: DeletionMode): boolean; //adds the real elements behind refs as received from getOrCreate to the container addLinks(context: Deserializer, ...refs: Ref[]): void { diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts index cf27268..c084149 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts @@ -36,11 +36,11 @@ export class ReLinkListContainer< return this._instance.map(i => ctx.get(i)) } - override remove(item: T): boolean { + override remove(item: T, mode: DeletionMode): boolean { const res = ListUpdater.removeFromList(item, this._instance) if (res) { if(this.inverseName !== undefined) { - item.removeFromReferencableContainer(this.inverseName, this._parent) + item.removeFromReferencableContainer(this.inverseName, this._parent, mode) } } return res; //todo behaviour of flag different to add?? @@ -50,10 +50,10 @@ export class ReLinkListContainer< ListUpdater.destructAllFromChangingList(this._instance, mode) } - override removeFromInverse(item: T): boolean { + override removeFromInverse(item: T, mode: DeletionMode): boolean { if(this.inverseName !== undefined) { for (const child of this._instance) { - child.removeFromReferencableContainer(this.inverseName, item) + child.removeFromReferencableContainer(this.inverseName, item, mode) } return true; // todo - refine? } diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts index cd3f964..962ccb1 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts @@ -21,7 +21,7 @@ export class ReLinkSingleContainer< protected set(instance: T): void { if(this.inverseName !== undefined) { - this._instance?.removeFromReferencableContainer(this.inverseName, this._parent) + this._instance?.removeFromReferencableContainer(this.inverseName, this._parent, DeletionMode.RELAXED) this._instance = instance; instance.addToReferencableContainer(this.inverseName, this._parent) } else { @@ -38,10 +38,10 @@ export class ReLinkSingleContainer< } } - remove(item: T): boolean { + remove(item: T, mode: DeletionMode): boolean { if(this._instance == item) { if (this.inverseName != undefined) { - item.removeFromReferencableContainer(this.inverseName, this._parent) + item.removeFromReferencableContainer(this.inverseName, this._parent, mode) } this._instance = undefined; return true; @@ -54,9 +54,9 @@ export class ReLinkSingleContainer< this._instance?.destruct(mode) } - removeFromInverse(item: T): boolean { + removeFromInverse(item: T, mode: DeletionMode): boolean { if(this.inverseName !== undefined) { - this._instance?.removeFromReferencableContainer(this.inverseName, item) + this._instance?.removeFromReferencableContainer(this.inverseName, item, mode) return true; // todo refine? } return false; diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index 4cd35b8..c3f06a8 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -18,7 +18,7 @@ export abstract class ReContainer< abstract add(item: T): boolean; - abstract remove(item: T): boolean; + abstract remove(item: T, mode: DeletionMode): boolean; //called to destruct all elements in the container (e.g. when destroying a parent abstract delete(mode: DeletionMode): void diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts index 39a17c7..7ab17ae 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts @@ -40,7 +40,7 @@ export class ReTreeListContainer> return false; } else { item.setParent(this); - oldParent?.remove(item) + oldParent?.remove(item, DeletionMode.RELAXED) return ListUpdater.addToListIfMissing(item, this._instance) } } diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts index 7c34e96..711df34 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts @@ -1,6 +1,7 @@ import {Referencable} from "../../referenceable"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {ReContainer} from "../re-container"; +import { DeletionMode } from "../../../../utils/deletion-mode"; export class ReTreeParentContainer> extends ReContainer { @@ -21,13 +22,13 @@ export class ReTreeParentContainer> let me: T = this._parent const currentParentCont = this._parent.parent if(currentParentCont != undefined) { - currentParentCont.remove(this._parent as T["ParentType"]) + currentParentCont.remove(this._parent as T["ParentType"], DeletionMode.RELAXED) } return item.addToReferencableContainer(this.inverseName, me) } - remove(item: T["ParentType"]): boolean { - return item.removeFromReferencableContainer(this.inverseName, this._parent) + remove(item: T["ParentType"], mode: DeletionMode): boolean { + return item.removeFromReferencableContainer(this.inverseName, this._parent, mode) } delete(): void {} diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index a7ec78f..0a2c7ba 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -34,7 +34,7 @@ export abstract class Referencable< setParent(parent: ReTreeChildrenContainer | undefined) { if(this.$parent) { - this.$parent.remove(this) + this.$parent.remove(this, DeletionMode.RELAXED) } this.$parent = parent; } @@ -60,9 +60,9 @@ export abstract class Referencable< } destruct(mode: DeletionMode) { - this.$parent?.remove(this) + this.$parent?.remove(this, mode) this.$otherReferences.forEach(refContainer => { - refContainer.removeFromInverse(this) + refContainer.removeFromInverse(this, mode) }) this.$treeChildren.forEach(child => { child.delete(mode) @@ -82,8 +82,8 @@ export abstract class Referencable< return this.getContainer(name).add(item) } - public removeFromReferencableContainer>(name: string, item: T): boolean { - return this.getContainer(name).remove(item) + public removeFromReferencableContainer>(name: string, item: T, mode: DeletionMode): boolean { + return this.getContainer(name).remove(item, mode) } toJson(ctxOPt?: SerializationContext): JsonOf { From 6b369cb53c9d0d258da9b67a9832384defcaac94 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Wed, 25 Feb 2026 18:55:22 +0100 Subject: [PATCH 06/22] added isRequired to ReContainer and the classes that inherit it --- .../referencable/container/link/re-link-container.ts | 4 ++-- .../referencable/container/link/re-link-list-container.ts | 4 ++-- .../referencable/container/link/re-link-single-container.ts | 4 ++-- .../lib/referencing/referencable/container/re-container.ts | 4 +++- .../container/tree/re-tree-children-container.ts | 4 ++-- .../referencable/container/tree/re-tree-list-container.ts | 6 +++--- .../referencable/container/tree/re-tree-parent-container.ts | 4 ++-- .../referencable/container/tree/re-tree-single-container.ts | 4 ++-- 8 files changed, 18 insertions(+), 16 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts index 93d8559..9acd501 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts @@ -12,8 +12,8 @@ export abstract class ReLinkContainer< readonly inverseName?: string; - protected constructor(parent: P, referenceName: string, inverseName?: string) { - super(parent, referenceName); + protected constructor(parent: P, referenceName: string, isRequired: boolean, inverseName?: string) { + super(parent, referenceName, isRequired); this.inverseName = inverseName; this._parent.$otherReferences.push(this) } diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts index c084149..1292076 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts @@ -12,8 +12,8 @@ export class ReLinkListContainer< readonly _instance: T[] = []; - constructor(parent: P, name: string, inverse?: string) { - super(parent, name, inverse); + constructor(parent: P, name: string, isRequired: boolean, inverse?: string) { + super(parent, name, isRequired, inverse); } override get(): T[] { diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts index 962ccb1..b259c95 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts @@ -11,8 +11,8 @@ export class ReLinkSingleContainer< _instance?: T - constructor(parent: P, referenceName: string, inverseName?: string ) { - super(parent, referenceName, inverseName); + constructor(parent: P, referenceName: string, isRequired: boolean = false, inverseName?: string) { + super(parent, referenceName, isRequired, inverseName); } get(): T | undefined { diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index c3f06a8..c6c251b 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -8,10 +8,12 @@ export abstract class ReContainer< > { readonly _parent: P; readonly referenceName: string; + readonly isRequired: boolean; - constructor(parent: P, referenceName: string, isRequired: boolean = false) { + constructor(parent: P, referenceName: string, isRequired: boolean) { this._parent = parent; this.referenceName = referenceName; + this.isRequired = isRequired; } abstract get(): T[] | T | undefined; diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-children-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-children-container.ts index 98742a7..021c866 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-children-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-children-container.ts @@ -10,8 +10,8 @@ export abstract class ReTreeChildrenContainer< readonly defaultEClass?: string; - protected constructor(parent: T["ParentType"], name: string, eClass?: string) { - super(parent, name); + protected constructor(parent: T["ParentType"], name: string, isRequired: boolean, eClass?: string) { + super(parent, name, isRequired); this.defaultEClass = eClass; this._parent.$treeChildren.push(this) } diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts index 7ab17ae..ed3582f 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts @@ -12,8 +12,8 @@ export class ReTreeListContainer> readonly _instance: T[] = []; - constructor(parent: T["ParentType"], name: string, _?: string, eClass?: string) { - super(parent, name, eClass); + constructor(parent: T["ParentType"], name: string, isRequired: boolean, _?: string, eClass?: string) { + super(parent, name, isRequired, eClass); } override get(): T[] { @@ -45,7 +45,7 @@ export class ReTreeListContainer> } } - override remove(item: T): boolean { + override remove(item: T, mode: DeletionMode): boolean { let removed = ListUpdater.removeFromList(item, this._instance) if(removed){ item.setParent(undefined); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts index 711df34..4da9d64 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.ts @@ -8,8 +8,8 @@ export class ReTreeParentContainer> inverseName: string; - constructor(parent: T, referenceName: string, inverseName: string ) { - super(parent, referenceName); // referenceName is actually unused for this container type + constructor(parent: T, referenceName: string, isRequired: boolean, inverseName: string) { + super(parent, referenceName, isRequired); // referenceName is actually unused for this container type this.inverseName = inverseName; } diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts index 3a64dbb..ae7bbda 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts @@ -11,8 +11,8 @@ export class ReTreeSingleContainer> _instance?: T - constructor(parent: T["ParentType"], referenceName: string, _?: string, eClass?: string) { - super(parent, referenceName, eClass); + constructor(parent: T["ParentType"], referenceName: string, isRequired: boolean, eClass?: string) { + super(parent, referenceName, isRequired, eClass); } override get(): T | undefined { From d454ea481d067d5720e99364182802dac0fb846d Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Wed, 25 Feb 2026 18:57:36 +0100 Subject: [PATCH 07/22] check for isRequired flag removeFromReferencableContainer() method and destruct if deletion mode is CASCADE --- .../src/lib/referencing/referencable/referenceable.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index 0a2c7ba..da11fc8 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -83,7 +83,15 @@ export abstract class Referencable< } public removeFromReferencableContainer>(name: string, item: T, mode: DeletionMode): boolean { - return this.getContainer(name).remove(item, mode) + let container = this.getContainer(name) + if (mode == DeletionMode.CASCADE && container.isRequired) { + const instance = container.get() + if (instance == undefined || (Array.isArray(instance) && instance.length == 0)) { + container._parent.destruct(mode) + return true; + } + } + return container.remove(item, mode) } toJson(ctxOPt?: SerializationContext): JsonOf { From 3d44b8064d4b31ed78dcaf37182d2f2fae54b2bc Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 3 Mar 2026 03:11:17 +0100 Subject: [PATCH 08/22] updated test files to be compatible with new isRequired flag --- .../link/re-link-list-container.spec.ts | 7 ++++--- .../tree/re-tree-list-container.spec.ts | 2 +- .../tree/re-tree-parent-container.spec.ts | 2 +- .../tree/re-tree-single-container.spec.ts | 2 +- .../test/re-containers-with-single-child.ts | 8 ++++---- .../test/referencables-with-children.ts | 19 ++++++++++--------- 6 files changed, 21 insertions(+), 19 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts index 609430c..e51bbdf 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts @@ -1,21 +1,22 @@ import { ReLinkListContainer } from './re-link-list-container'; import {ReferencableTester} from "../../../test/referencable-tester"; import {RootWithChildren, ReChild3} from "../../../test/referencables-with-children"; +import { DeletionMode } from '../../../../utils/deletion-mode'; describe('ReLinkListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReLinkListContainer(tester, 'refName')).toBeTruthy(); + expect(new ReLinkListContainer(tester, 'refName', false)).toBeTruthy(); }); it("should give true if the remove and remove inverse chain triggered an element removal", () => { let tester = new RootWithChildren() let elem2 = new ReChild3() - expect(tester._link3.remove(elem2)).toBeFalse() + expect(tester._link3.remove(elem2, DeletionMode.RELAXED)).toBeFalse() tester.addLink3(elem2) expect(tester._link3.get().length).toBe(1) expect(tester._link3.get()).toContain(elem2) - expect(tester._link3.remove(elem2)).toBeTrue() + expect(tester._link3.remove(elem2, DeletionMode.RELAXED)).toBeTrue() expect(tester._link3.get().length).toBe(0) }) }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts index 4e47c5f..9058bd5 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester} from "../../../test/referencable-tester"; describe('ReferencableTreeListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReTreeListContainer(tester, 'refName')).toBeTruthy(); + expect(new ReTreeListContainer(tester, 'refName', false)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.spec.ts index 2d4beb4..e2b5ec7 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-parent-container.spec.ts @@ -4,7 +4,7 @@ import {ReferencableTester} from "../../../test/referencable-tester"; describe('ReferencableTreeParentContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReTreeParentContainer(tester, 'refName', 'necessary')).toBeTruthy(); + expect(new ReTreeParentContainer(tester, 'refName', false, 'necessary')).toBeTruthy(); }); it('should serialize a contained ref', () => { diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts index 512a217..0422c35 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester} from "../../../test/referencable-tester"; describe('ReferencableTreeSingletonContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReTreeSingleContainer(tester, 'test')).toBeTruthy(); + expect(new ReTreeSingleContainer(tester, 'test', false)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts b/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts index 83f2805..ed06d4f 100644 --- a/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts +++ b/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts @@ -38,8 +38,8 @@ export class ReContainersWithSingleChild extends Referencable { constructor() { super(); - this._child = new ReTreeSingleContainer(this, ReContainersWithSingleChild.$childName) - this._link = new ReLinkSingleContainer(this, ReContainersWithSingleChild.$linkName, ReSingleChildExample.$otherLinkName) + this._child = new ReTreeSingleContainer(this, ReContainersWithSingleChild.$childName, false) + this._link = new ReLinkSingleContainer(this, ReContainersWithSingleChild.$linkName, false, ReSingleChildExample.$otherLinkName) } static fromJSON (convJson: JsonOf): ReContainersWithSingleChild { @@ -76,7 +76,7 @@ export class ReSingleChildExample extends Referencable { constructor() { super(); - this._child2 = new ReTreeListContainer(this, RootWithChildren.$child2Name); - this._link3 = new ReLinkListContainer(this, RootWithChildren.$link3Name, ReChild3.$link1Name); + this._child2 = new ReTreeListContainer(this, RootWithChildren.$child2Name, false); + this._link3 = new ReLinkListContainer(this, RootWithChildren.$link3Name, false, ReChild3.$link1Name); } get child2(): Middle2WithChildren[] { @@ -37,7 +38,7 @@ export class RootWithChildren extends Referencable { c2s.map(c2 => this._child2.add(c2)) } removeChild2(...c2s: Middle2WithChildren[]) { - c2s.map(c2 => this._child2.remove(c2)) + c2s.map(c2 => this._child2.remove(c2, DeletionMode.RELAXED)) } get link3() { @@ -47,7 +48,7 @@ export class RootWithChildren extends Referencable { c3s.map(c3 => this._link3.add(c3)) } removeLink3(...c3s: ReChild3[]) { - c3s.map(c3 => this._link3.remove(c3)) + c3s.map(c3 => this._link3.remove(c3, DeletionMode.RELAXED)) } } @@ -62,7 +63,7 @@ export class Middle2WithChildren extends Referencable { constructor() { super(); - this._child3 = new ReTreeListContainer(this, Middle2WithChildren.$child3Name) + this._child3 = new ReTreeListContainer(this, Middle2WithChildren.$child3Name, false) } get child3(): ReChild3[] { @@ -72,7 +73,7 @@ export class Middle2WithChildren extends Referencable { c3s.map(c3 => this._child3.add(c3)) } removeChild3(...c3s: ReChild3[]) { - c3s.map(c3 => this._child3.remove(c3)) + c3s.map(c3 => this._child3.remove(c3, DeletionMode.RELAXED)) } } @@ -89,8 +90,8 @@ export class ReChild3 extends Referencable { constructor() { super(); - this._link1 = new ReLinkListContainer(this, ReChild3.$link1Name, RootWithChildren.$link3Name) - this._parentPointer = new ReTreeParentContainer(this, ReChild3.$parentPointerName, Middle2WithChildren.$child3Name) + this._link1 = new ReLinkListContainer(this, ReChild3.$link1Name, false, RootWithChildren.$link3Name) + this._parentPointer = new ReTreeParentContainer(this, ReChild3.$parentPointerName, false, Middle2WithChildren.$child3Name) } get link1(): RootWithChildren[] { return this._link1.get() @@ -99,7 +100,7 @@ export class ReChild3 extends Referencable { c1s.map(c1 => this._link1.add(c1)) } removeLink1(...c1s: RootWithChildren[]) { - c1s.map(c1 => this._link1.remove(c1)) + c1s.map(c1 => this._link1.remove(c1, DeletionMode.RELAXED)) } get parentPointer(): Middle2WithChildren | undefined { From 370b45d1f1a8b59e4044a0816b9b08c96bee8fe7 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 3 Mar 2026 03:15:25 +0100 Subject: [PATCH 09/22] adapted removeFromReferenceableContainer, destructAllFromChangingList and delete for different deletion behaviours according to deletion mode --- .../referencable/container/tree/re-tree-single-container.ts | 6 +++++- .../src/lib/referencing/referencable/referenceable.ts | 6 +++--- projects/emfular/src/lib/utils/list-updater.ts | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts index ae7bbda..ff31342 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts @@ -47,7 +47,11 @@ export class ReTreeSingleContainer> } override delete(mode: DeletionMode) { - this._instance?.destruct(mode) + if (mode === DeletionMode.CASCADE) { + this._instance?.destruct(mode) + } else if (mode === DeletionMode.RELAXED) { + this._instance?.parent?.remove(this._instance, mode) + } } fromJson(formerPrefix: string, context: Deserializer, json: any) { diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index da11fc8..29710aa 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -84,14 +84,14 @@ export abstract class Referencable< public removeFromReferencableContainer>(name: string, item: T, mode: DeletionMode): boolean { let container = this.getContainer(name) - if (mode == DeletionMode.CASCADE && container.isRequired) { + let result = container.remove(item, mode) + if (result && mode == DeletionMode.CASCADE && container.isRequired) { const instance = container.get() if (instance == undefined || (Array.isArray(instance) && instance.length == 0)) { container._parent.destruct(mode) - return true; } } - return container.remove(item, mode) + return result } toJson(ctxOPt?: SerializationContext): JsonOf { diff --git a/projects/emfular/src/lib/utils/list-updater.ts b/projects/emfular/src/lib/utils/list-updater.ts index c2448e2..a09400d 100644 --- a/projects/emfular/src/lib/utils/list-updater.ts +++ b/projects/emfular/src/lib/utils/list-updater.ts @@ -28,7 +28,11 @@ export class ListUpdater { static destructAllFromChangingList>(list: T[], mode: DeletionMode) { while(list?.length > 0){ - list[0].destruct(mode) + if (mode === DeletionMode.CASCADE) { + list[0].destruct(mode) + } else if (mode === DeletionMode.RELAXED) { + list[0].parent?.remove(list[0], mode) + } } } From a75705db0155775534e5dde071b7adc7ed2609d8 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 3 Mar 2026 03:16:29 +0100 Subject: [PATCH 10/22] added minimal keml for testing deletion modes --- .../referencing/test/min-keml-example.spec.ts | 18 + .../lib/referencing/test/min-keml-example.ts | 434 ++++++++++++++++++ 2 files changed, 452 insertions(+) create mode 100644 projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts create mode 100644 projects/emfular/src/lib/referencing/test/min-keml-example.ts diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts new file mode 100644 index 0000000..4b87c87 --- /dev/null +++ b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts @@ -0,0 +1,18 @@ +import { DeletionMode } from "../../utils/deletion-mode"; +import { ConversationPartner, InformationLink, InformationLinkType, NewInformation, Preknowledge, ReceiveMessage, SendMessage } from "./min-keml-example"; + +describe('MinKemlExample', () => { + it('should work', () => { + const recMessA = ReceiveMessage.create(new ConversationPartner(), 0, 'Received content A'); + const recMessB = ReceiveMessage.create(new ConversationPartner(), 1, 'Received content B'); + const newInfoA = NewInformation.create(recMessA, 'New info A'); + const newInfoB = new NewInformation(); + newInfoA.addRepeatedBy(recMessB); + const sendMess = SendMessage.create(new ConversationPartner(), 2, 'Send content'); + newInfoA.addIsUsedOn(sendMess); + const infoLinkAB = InformationLink.create(newInfoA, newInfoB, InformationLinkType.SUPPORT, 'Linking A to B'); + const preKnow = Preknowledge.create('Preknowledge'); + const infoLinkPreA = InformationLink.create(preKnow, newInfoA, InformationLinkType.ATTACK, 'Preknowledge attacking A'); + newInfoA.destruct(DeletionMode.RELAXED); + }); +}); \ No newline at end of file diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.ts b/projects/emfular/src/lib/referencing/test/min-keml-example.ts new file mode 100644 index 0000000..58c204b --- /dev/null +++ b/projects/emfular/src/lib/referencing/test/min-keml-example.ts @@ -0,0 +1,434 @@ +import { DeletionMode } from "../../utils/deletion-mode"; +import { ReLinkListContainer } from "../referencable/container/link/re-link-list-container"; +import { ReLinkSingleContainer } from "../referencable/container/link/re-link-single-container"; +import { ReTreeListContainer } from "../referencable/container/tree/re-tree-list-container"; +import { ReTreeParentContainer } from "../referencable/container/tree/re-tree-parent-container"; +import { ReTreeSingleContainer } from "../referencable/container/tree/re-tree-single-container"; +import { Referencable } from "../referencable/referenceable"; + +export enum InformationLinkType { + SUPPLEMENT = 'SUPPLEMENT', + SUPPORT = 'SUPPORT', + STRONG_SUPPORT = 'STRONG_SUPPORT', + ATTACK = 'ATTACK', + STRONG_ATTACK = 'STRONG_ATTACK', +} + +export class Conversation extends Referencable { + static readonly $authorName = 'author'; + static readonly $conversationPartnersName = 'conversationPartners'; + + title: string; + + _author: ReTreeSingleContainer; + get author(): Author { + return this._author.get()!! + } + set author(author: Author) { + this._author.add(author); + } + _conversationPartners: ReTreeListContainer; + get conversationPartners(): ConversationPartner[] { + return this._conversationPartners.get() + } + addCP(...cps: ConversationPartner[]) { + cps.map(cp => { + this._conversationPartners.add(cp) + }) + } + + constructor( + title: string = 'New Conversation' + ) { + super(); + this._author = new ReTreeSingleContainer(this, Conversation.$authorName, false); + this._conversationPartners = new ReTreeListContainer(this, Conversation.$conversationPartnersName, false); + this.title = title; + this.author = new Author(); + } + + static create(title: string = 'New conversation', author?: Author): Conversation { + const conv = new Conversation('New Conversation'); + conv.title = title; + conv.author = author? author: new Author(); + return conv; + } + +} + +export abstract class LifeLine extends Referencable{ + name: string; + xPosition: number; //int todo + + protected constructor(name?: string, xPosition: number = 0) { + super(); + this.name = name? name: ''; + this.xPosition = xPosition; + } + +} + +export class ConversationPartner extends LifeLine { + + constructor(name: string = 'NewPartner', xPosition?: number) { + super(name, xPosition); + } + + static create(name: string = 'NewPartner', xPosition?: number): ConversationPartner { + const cp = new ConversationPartner() + cp.name = name + cp.xPosition = xPosition? xPosition : 0; + return cp; + } + +} + +export class Author extends LifeLine{ + static readonly $preknowledgeName: string = 'preknowledge'; + static readonly $messagesName: string = 'messages'; + + _preknowledge: ReTreeListContainer; + get preknowledge(): Preknowledge[] { + return this._preknowledge.get() + } + addPreknowledge(...preknowledge: Preknowledge[]) { + preknowledge.map(p => { + this._preknowledge.add(p) + }) + } + + _messages: ReTreeListContainer; + get messages(): Message[] { + return this._messages.get() + } + addMessage(...msgs: Message[]) { + msgs.map(m => { + this._messages.add(m) + }) + } + + constructor() { + super(); + this._preknowledge = new ReTreeListContainer(this, Author.$preknowledgeName, false) + this._messages = new ReTreeListContainer(this, Author.$messagesName, false) + } + + static create(name?: string, xPosition: number = 0): Author { + const auth = new Author() + auth.name = name? name: '' + auth.xPosition = xPosition + return auth + } + +} + +export abstract class Message extends Referencable { + public static readonly $counterPartName = 'counterPart' + + _counterPart: ReLinkSingleContainer; + get counterPart(): ConversationPartner { + return this._counterPart.get()!! //todo + } + set counterPart(value: ConversationPartner) { + this._counterPart.add(value); + } + + timing: number; + content: string; + originalContent?: string; + + protected constructor( + timing: number = 0, + content: string = "", + originalContent?: string, + ) { + super(); + this.timing = timing; + this.content = content; + this.originalContent = originalContent; + this._counterPart = new ReLinkSingleContainer(this, Message.$counterPartName) + } + + static isSend(eClass: string) { + return eClass.endsWith("SendMessage"); + } + + isSend(): this is SendMessage { + return this instanceof SendMessage + } + + isReceive(): this is ReceiveMessage { + return this instanceof ReceiveMessage + } + + static newMessage(isSend: boolean, counterPart: ConversationPartner, timing: number, content: string, originalContent: string = 'Original content'): Message { + if (isSend) { + return SendMessage.create(counterPart, timing, content, originalContent) + } else { + return ReceiveMessage.create(counterPart, timing, content, originalContent) + } + } +} + + +export class SendMessage extends Message { + public static readonly $usesName = 'uses' + + private readonly _uses: ReLinkListContainer; + get uses(): Information[] { + return this._uses.get(); + } + addUsage(info: Information) { + this._uses.add(info) + } + removeUsage(info: Information, mode: DeletionMode = DeletionMode.RELAXED): boolean { + return this._uses.remove(info, mode) + } + + constructor( + timing?: number, + content: string = 'New send content', + originalContent?: string, + ) { + super(timing, content, originalContent); + this._uses = new ReLinkListContainer(this, SendMessage.$usesName, false, Information.$isUsedOnName); + } + + static create(counterPart: ConversationPartner, + timing: number, + content: string = 'New send content', + originalContent?: string, + ): SendMessage { + const send = new SendMessage(timing, content, originalContent); + send.counterPart = counterPart; + return send; + } + +} + +export class ReceiveMessage extends Message { + static readonly $generatesName: string = 'generates'; + static readonly $repeatsName: string = 'repeats'; + + _generates: ReTreeListContainer; + get generates(): NewInformation[] { + return this._generates.get()!! + } + + _repeats: ReLinkListContainer; + get repeats(): Information[] { + return this._repeats.get(); + } + addRepetition(info: Information) { + this._repeats.add(info); + } + removeRepetition(info: Information, mode: DeletionMode = DeletionMode.RELAXED): boolean { + return this._repeats.remove(info, mode); + } + + isInterrupted: boolean = false; + + constructor( + timing?: number, + content: string = "New receive content", + originalContent?: string, + isInterrupted: boolean = false, + ) { + super(timing, content, originalContent); + this._generates = new ReTreeListContainer(this, ReceiveMessage.$generatesName, false, NewInformation.$sourceName); + this._repeats = new ReLinkListContainer(this, ReceiveMessage.$repeatsName, false, Information.$repeatedByName); + this.isInterrupted = isInterrupted; + } + + static create(counterPart: ConversationPartner, + timing: number, + content?: string, + originalContent?: string, + isInterrupted: boolean = false,): ReceiveMessage { + const rec = new ReceiveMessage(timing, content, originalContent, isInterrupted); + rec.counterPart = counterPart; + return rec + } + +} + +export abstract class Information< + P extends Referencable=Referencable +> extends Referencable

{ + + message: string = ""; + isInstruction: boolean = false; + initialTrust: number | undefined; + currentTrust: number | undefined; + feltTrustImmediately: number | undefined; + feltTrustAfterwards: number | undefined; + + abstract getTiming(): number; + + static readonly $causesName: string = 'causes' + static readonly $isUsedOnName: string = 'isUsedOn' + static readonly $repeatedByName: string = 'repeatedBy' + static readonly $targetedByName: string = 'targetedBy' + readonly _causes: ReTreeListContainer; + get causes(): InformationLink[] { + return this._causes.get(); + } + + readonly _targetedBy: ReLinkListContainer + get targetedBy(): InformationLink[] { + return this._targetedBy.get(); + } + + readonly _isUsedOn: ReLinkListContainer + get isUsedOn(): SendMessage[] { + return this._isUsedOn.get(); + } + addIsUsedOn(...send: SendMessage[]){ + send.map(s => this._isUsedOn.add(s)) + } + removeIsUsedOn(send: SendMessage, mode: DeletionMode = DeletionMode.RELAXED){ + this._isUsedOn.remove(send, mode) + } + + readonly _repeatedBy: ReLinkListContainer + get repeatedBy(): ReceiveMessage[] { + return this._repeatedBy.get(); + } + + addRepeatedBy(msg: ReceiveMessage) { + this._repeatedBy.add(msg) + } + removeRepeatedBy(msg: ReceiveMessage, mode: DeletionMode = DeletionMode.RELAXED) { + this._repeatedBy.remove(msg, mode) + } + + protected constructor() { + super(); + + this._causes = new ReTreeListContainer(this, NewInformation.$causesName, false, InformationLink.$sourceName); + this._targetedBy = new ReLinkListContainer(this, Information.$targetedByName, false, InformationLink.$targetName) + this._isUsedOn = new ReLinkListContainer(this, 'isUsedOn', false, 'uses'); + this._repeatedBy = new ReLinkListContainer(this, NewInformation.$repeatedByName, false, ReceiveMessage.$repeatsName); + } + + abstract duplicate(): Information; + +} +export class NewInformation extends Information { + + public static readonly $sourceName = 'source' + + readonly _source: ReTreeParentContainer; + set source(rec: ReceiveMessage) { + this._source.add(rec) + } + get source(): ReceiveMessage { + return this._source.get()!! + } + + override getTiming(): number { + return this.source.timing + } + + constructor() { + super(); + this._source = new ReTreeParentContainer(this, NewInformation.$sourceName, false, ReceiveMessage.$generatesName); + } + + override duplicate(): NewInformation { + return NewInformation.create(this.source, 'Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); + } + + static create(source: ReceiveMessage, + message: string, isInstruction: boolean = false, + initialTrust?: number, currentTrust?: number, feltTrustImmediately?: number, feltTrustAfterwards?: number,): NewInformation { + const info = new NewInformation(); + info.source = source; + info.message = message; + info.isInstruction = isInstruction; + info.initialTrust = initialTrust; + info.currentTrust = currentTrust; + info.feltTrustImmediately = feltTrustImmediately; + info.feltTrustAfterwards = feltTrustAfterwards; + return info; + } + +} + +export class Preknowledge extends Information { + + constructor() { + super(); + } + + getTiming(): number { + let timing; + if (this.isUsedOn?.length >0) { + timing = Math.min(...this.isUsedOn.map(send => send.timing)); + } else { + timing = 0 + } + return timing + } + + override duplicate(): Preknowledge { + return Preknowledge.create('Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); + } + + static create(message: string = 'Preknowledge', isInstruction: boolean = false, + initialTrust?: number, currentTrust?: number, + feltTrustImmediately?: number, feltTrustAfterwards?: number): Preknowledge { + const pre = new Preknowledge() + pre.message = message + pre.isInstruction = isInstruction + pre.initialTrust = initialTrust + pre.currentTrust = currentTrust + pre.feltTrustImmediately = feltTrustImmediately + pre.feltTrustAfterwards = feltTrustAfterwards + return pre + } + +} + +export class InformationLink extends Referencable { + + public static readonly $sourceName = 'source' + public static readonly $targetName = 'target' + readonly _source: ReTreeParentContainer + get source(): Information { + return this._source.get()!!; //todo + } + set source(source: Information) { + this._source.add(source) + } + + readonly _target: ReLinkSingleContainer + get target(): Information { + return this._target.get()!!; + } + set target(target: Information) { + this._target.add(target); + } + + type: InformationLinkType = InformationLinkType.SUPPLEMENT; + linkText?: string; + + constructor() { + super(); + this._source = new ReTreeParentContainer(this, InformationLink.$sourceName, false, NewInformation.$causesName); + this._target = new ReLinkSingleContainer(this, InformationLink.$targetName, true, Information.$targetedByName); + } + + static create(source: Information, target: Information, type: InformationLinkType, linkText?: string,): InformationLink { + const link = new InformationLink() + link.source = source + link.target = target; + link.type = type + link.linkText = linkText + return link + } + +} + + + From 4509e84430f68906ca2d956b20ec4c49853dc170 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 10 Mar 2026 01:31:28 +0100 Subject: [PATCH 11/22] Updated tests and potential bug fix in removeFromInverse() --- .../container/link/re-link-list-container.ts | 2 +- .../referencing/test/min-keml-example.spec.ts | 64 +++++++++++++++---- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts index 1292076..d010a8a 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts @@ -52,7 +52,7 @@ export class ReLinkListContainer< override removeFromInverse(item: T, mode: DeletionMode): boolean { if(this.inverseName !== undefined) { - for (const child of this._instance) { + for (const child of [...this._instance]) { child.removeFromReferencableContainer(this.inverseName, item, mode) } return true; // todo - refine? diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts index 4b87c87..c091e07 100644 --- a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts +++ b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts @@ -2,17 +2,59 @@ import { DeletionMode } from "../../utils/deletion-mode"; import { ConversationPartner, InformationLink, InformationLinkType, NewInformation, Preknowledge, ReceiveMessage, SendMessage } from "./min-keml-example"; describe('MinKemlExample', () => { - it('should work', () => { - const recMessA = ReceiveMessage.create(new ConversationPartner(), 0, 'Received content A'); - const recMessB = ReceiveMessage.create(new ConversationPartner(), 1, 'Received content B'); - const newInfoA = NewInformation.create(recMessA, 'New info A'); - const newInfoB = new NewInformation(); - newInfoA.addRepeatedBy(recMessB); - const sendMess = SendMessage.create(new ConversationPartner(), 2, 'Send content'); - newInfoA.addIsUsedOn(sendMess); - const infoLinkAB = InformationLink.create(newInfoA, newInfoB, InformationLinkType.SUPPORT, 'Linking A to B'); - const preKnow = Preknowledge.create('Preknowledge'); - const infoLinkPreA = InformationLink.create(preKnow, newInfoA, InformationLinkType.ATTACK, 'Preknowledge attacking A'); + it('relaxed deletion of NewInformation should delete reference to source', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); newInfoA.destruct(DeletionMode.RELAXED); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + }); + + it('cascaded deletion of NewInformation should delete reference to source', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + newInfoA.destruct(DeletionMode.CASCADE); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + }); + + it('relaxed deletion of NewInformation should delete reference to isUsedOn', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); + let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); + sendMessA.addUsage(newInfoA); + sendMessA.addUsage(newInfoB); + sendMessB.addUsage(newInfoA); + sendMessB.addUsage(newInfoB); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + expect(newInfoA.isUsedOn.length).toBe(2); + expect(newInfoB.isUsedOn.length).toBe(2); + expect(sendMessA.uses.length).toBe(2); + expect(sendMessB.uses.length).toBe(2); + newInfoA.destruct(DeletionMode.RELAXED); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + expect(newInfoA.isUsedOn.length).toBe(0); + expect(newInfoB.isUsedOn.length).toBe(2); + expect(sendMessA.uses.length).toBe(1); + expect(sendMessB.uses.length).toBe(1); }); }); \ No newline at end of file From a16c889b2016539ccdad11305889856f7dd51ab4 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 10 Mar 2026 02:41:31 +0100 Subject: [PATCH 12/22] Finished tests for new deletion modes --- .../referencing/test/min-keml-example.spec.ts | 196 +++++++++++++++++- 1 file changed, 194 insertions(+), 2 deletions(-) diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts index c091e07..7fb1739 100644 --- a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts +++ b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts @@ -1,5 +1,13 @@ -import { DeletionMode } from "../../utils/deletion-mode"; -import { ConversationPartner, InformationLink, InformationLinkType, NewInformation, Preknowledge, ReceiveMessage, SendMessage } from "./min-keml-example"; +import {DeletionMode} from "../../utils/deletion-mode"; +import { + Author, + ConversationPartner, + InformationLink, + InformationLinkType, + NewInformation, + ReceiveMessage, + SendMessage +} from "./min-keml-example"; describe('MinKemlExample', () => { it('relaxed deletion of NewInformation should delete reference to source', () => { @@ -37,10 +45,13 @@ describe('MinKemlExample', () => { let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); + let author: Author = Author.create('Author'); + author.addMessage(sendMessA, sendMessB); sendMessA.addUsage(newInfoA); sendMessA.addUsage(newInfoB); sendMessB.addUsage(newInfoA); sendMessB.addUsage(newInfoB); + expect(author.messages.length).toBe(2); expect(newInfoA.source).toBeDefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(2); @@ -49,6 +60,7 @@ describe('MinKemlExample', () => { expect(sendMessA.uses.length).toBe(2); expect(sendMessB.uses.length).toBe(2); newInfoA.destruct(DeletionMode.RELAXED); + expect(author.messages.length).toBe(2); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(1); @@ -57,4 +69,184 @@ describe('MinKemlExample', () => { expect(sendMessA.uses.length).toBe(1); expect(sendMessB.uses.length).toBe(1); }); + + it('cascaded deletion of NewInformation should delete reference to isUsedOn', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); + let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); + let author: Author = Author.create('Author'); + author.addMessage(sendMessA, sendMessB); + sendMessA.addUsage(newInfoA); + sendMessA.addUsage(newInfoB); + sendMessB.addUsage(newInfoA); + sendMessB.addUsage(newInfoB); + expect(author.messages.length).toBe(2); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + expect(newInfoA.isUsedOn.length).toBe(2); + expect(newInfoB.isUsedOn.length).toBe(2); + expect(sendMessA.uses.length).toBe(2); + expect(sendMessB.uses.length).toBe(2); + newInfoA.destruct(DeletionMode.CASCADE); + expect(author.messages.length).toBe(2); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + expect(newInfoA.isUsedOn.length).toBe(0); + expect(newInfoB.isUsedOn.length).toBe(2); + expect(sendMessA.uses.length).toBe(1); + expect(sendMessB.uses.length).toBe(1); + }); + + it('relaxed deletion of NewInformation should delete reference to repeatedBy', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); + let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + recMessRepA.addRepetition(newInfoA); + recMessRepA.addRepetition(newInfoB); + recMessRepB.addRepetition(newInfoA); + recMessRepB.addRepetition(newInfoB); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + expect(recMessRepA.repeats.length).toBe(2); + expect(recMessRepB.repeats.length).toBe(2); + expect(newInfoA.repeatedBy.length).toBe(2); + expect(newInfoB.repeatedBy.length).toBe(2); + newInfoA.destruct(DeletionMode.RELAXED); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + expect(recMessRepA.repeats.length).toBe(1); + expect(recMessRepB.repeats.length).toBe(1); + expect(newInfoA.repeatedBy.length).toBe(0); + expect(newInfoB.repeatedBy.length).toBe(2); + }); + + it('cascaded deletion of NewInformation should delete reference to repeatedBy', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); + let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + recMessRepA.addRepetition(newInfoA); + recMessRepA.addRepetition(newInfoB); + recMessRepB.addRepetition(newInfoA); + recMessRepB.addRepetition(newInfoB); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + expect(recMessRepA.repeats.length).toBe(2); + expect(recMessRepB.repeats.length).toBe(2); + expect(newInfoA.repeatedBy.length).toBe(2); + expect(newInfoB.repeatedBy.length).toBe(2); + newInfoA.destruct(DeletionMode.CASCADE); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + expect(recMessRepA.repeats.length).toBe(1); + expect(recMessRepB.repeats.length).toBe(1); + expect(newInfoA.repeatedBy.length).toBe(0); + expect(newInfoB.repeatedBy.length).toBe(2); + }); + + it('relaxed deletion of NewInformation should delete reference to causes but not delete children', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); + let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); + let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); + expect(newInfoA.source).toBeDefined(); + expect(newInfoA.causes.length).toBe(2); + expect(infoLinkAB.source).toBeDefined(); + expect(infoLinkAC.source).toBeDefined(); + expect(infoLinkAB.target).toBeDefined(); + expect(infoLinkAC.target).toBeDefined(); + newInfoA.destruct(DeletionMode.RELAXED); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoA.causes.length).toBe(0); + expect(infoLinkAB.source).toBeUndefined(); + expect(infoLinkAC.source).toBeUndefined(); + expect(infoLinkAB.target).toBeDefined(); + expect(infoLinkAC.target).toBeDefined(); + }); + + it('cascaded deletion of NewInformation should delete reference to causes and delete children', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); + let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); + let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); + expect(newInfoA.source).toBeDefined(); + expect(newInfoA.causes.length).toBe(2); + expect(infoLinkAB.source).toBeDefined(); + expect(infoLinkAC.source).toBeDefined(); + expect(infoLinkAB.target).toBeDefined(); + expect(infoLinkAC.target).toBeDefined(); + newInfoA.destruct(DeletionMode.CASCADE); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoA.causes.length).toBe(0); + expect(infoLinkAB.source).toBeUndefined(); + expect(infoLinkAC.source).toBeUndefined(); + expect(infoLinkAB.target).toBeUndefined(); + expect(infoLinkAC.target).toBeUndefined(); + }); + + it('relaxed deletion of NewInformation should delete reference to targetedBy but not delete children', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); + let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); + let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); + expect(newInfoA.source).toBeDefined(); + expect(newInfoA.targetedBy.length).toBe(2); + expect(infoLinkBA.source).toBeDefined(); + expect(infoLinkCA.source).toBeDefined(); + expect(infoLinkBA.target).toBeDefined(); + expect(infoLinkCA.target).toBeDefined(); + newInfoA.destruct(DeletionMode.RELAXED); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoA.targetedBy.length).toBe(0); + expect(infoLinkBA.source).toBeDefined(); + expect(infoLinkCA.source).toBeDefined(); + expect(infoLinkBA.target).toBeUndefined(); + expect(infoLinkCA.target).toBeUndefined(); + }); + + it('cascaded deletion of NewInformation should delete reference to targetedBy but not delete children', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); + let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); + let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); + expect(newInfoA.source).toBeDefined(); + expect(newInfoA.targetedBy.length).toBe(2); + expect(infoLinkBA.source).toBeDefined(); + expect(infoLinkCA.source).toBeDefined(); + expect(infoLinkBA.target).toBeDefined(); + expect(infoLinkCA.target).toBeDefined(); + newInfoA.destruct(DeletionMode.CASCADE); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoA.targetedBy.length).toBe(0); + expect(infoLinkBA.source).toBeUndefined(); + expect(infoLinkCA.source).toBeUndefined(); + expect(infoLinkBA.target).toBeUndefined(); + expect(infoLinkCA.target).toBeUndefined(); + }); }); \ No newline at end of file From 0342d967618d0064f12f4d1055574ffec5304c1d Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 10 Mar 2026 11:52:08 +0100 Subject: [PATCH 13/22] updated test name to fix inconsistency --- .../emfular/src/lib/referencing/test/min-keml-example.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts index 7fb1739..e73a9cc 100644 --- a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts +++ b/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts @@ -227,7 +227,7 @@ describe('MinKemlExample', () => { expect(infoLinkCA.target).toBeUndefined(); }); - it('cascaded deletion of NewInformation should delete reference to targetedBy but not delete children', () => { + it('cascaded deletion of NewInformation should delete reference to targetedBy and delete children', () => { let partner: ConversationPartner = new ConversationPartner(); let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); From 9d9a121c9776aa3741e04e4920ad66e1c560d650 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 17 Mar 2026 14:10:47 +0100 Subject: [PATCH 14/22] renamed test files --- ...e.spec.ts => conversation-example.spec.ts} | 41 +++++++++++++------ ...eml-example.ts => conversation-example.ts} | 10 ++--- 2 files changed, 34 insertions(+), 17 deletions(-) rename projects/emfular/src/lib/referencing/test/{min-keml-example.spec.ts => conversation-example.spec.ts} (89%) rename projects/emfular/src/lib/referencing/test/{min-keml-example.ts => conversation-example.ts} (96%) diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts similarity index 89% rename from projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts rename to projects/emfular/src/lib/referencing/test/conversation-example.spec.ts index e73a9cc..60497d8 100644 --- a/projects/emfular/src/lib/referencing/test/min-keml-example.spec.ts +++ b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts @@ -1,4 +1,4 @@ -import {DeletionMode} from "../../utils/deletion-mode"; +import {DeletionAdditionMode} from "../../utils/deletion-addition-mode"; import { Author, ConversationPartner, @@ -7,7 +7,7 @@ import { NewInformation, ReceiveMessage, SendMessage -} from "./min-keml-example"; +} from "./conversation-example"; describe('MinKemlExample', () => { it('relaxed deletion of NewInformation should delete reference to source', () => { @@ -18,7 +18,7 @@ describe('MinKemlExample', () => { expect(newInfoA.source).toBeDefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(2); - newInfoA.destruct(DeletionMode.RELAXED); + newInfoA.destruct(DeletionAdditionMode.RELAXED); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(1); @@ -32,7 +32,7 @@ describe('MinKemlExample', () => { expect(newInfoA.source).toBeDefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(2); - newInfoA.destruct(DeletionMode.CASCADE); + newInfoA.destruct(DeletionAdditionMode.STRICT); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(1); @@ -59,7 +59,7 @@ describe('MinKemlExample', () => { expect(newInfoB.isUsedOn.length).toBe(2); expect(sendMessA.uses.length).toBe(2); expect(sendMessB.uses.length).toBe(2); - newInfoA.destruct(DeletionMode.RELAXED); + newInfoA.destruct(DeletionAdditionMode.RELAXED); expect(author.messages.length).toBe(2); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); @@ -91,7 +91,7 @@ describe('MinKemlExample', () => { expect(newInfoB.isUsedOn.length).toBe(2); expect(sendMessA.uses.length).toBe(2); expect(sendMessB.uses.length).toBe(2); - newInfoA.destruct(DeletionMode.CASCADE); + newInfoA.destruct(DeletionAdditionMode.STRICT); expect(author.messages.length).toBe(2); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); @@ -120,7 +120,7 @@ describe('MinKemlExample', () => { expect(recMessRepB.repeats.length).toBe(2); expect(newInfoA.repeatedBy.length).toBe(2); expect(newInfoB.repeatedBy.length).toBe(2); - newInfoA.destruct(DeletionMode.RELAXED); + newInfoA.destruct(DeletionAdditionMode.RELAXED); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(1); @@ -148,7 +148,7 @@ describe('MinKemlExample', () => { expect(recMessRepB.repeats.length).toBe(2); expect(newInfoA.repeatedBy.length).toBe(2); expect(newInfoB.repeatedBy.length).toBe(2); - newInfoA.destruct(DeletionMode.CASCADE); + newInfoA.destruct(DeletionAdditionMode.STRICT); expect(newInfoA.source).toBeUndefined(); expect(newInfoB.source).toBeDefined(); expect(recMess.generates.length).toBe(1); @@ -172,7 +172,7 @@ describe('MinKemlExample', () => { expect(infoLinkAC.source).toBeDefined(); expect(infoLinkAB.target).toBeDefined(); expect(infoLinkAC.target).toBeDefined(); - newInfoA.destruct(DeletionMode.RELAXED); + newInfoA.destruct(DeletionAdditionMode.RELAXED); expect(newInfoA.source).toBeUndefined(); expect(newInfoA.causes.length).toBe(0); expect(infoLinkAB.source).toBeUndefined(); @@ -195,7 +195,7 @@ describe('MinKemlExample', () => { expect(infoLinkAC.source).toBeDefined(); expect(infoLinkAB.target).toBeDefined(); expect(infoLinkAC.target).toBeDefined(); - newInfoA.destruct(DeletionMode.CASCADE); + newInfoA.destruct(DeletionAdditionMode.STRICT); expect(newInfoA.source).toBeUndefined(); expect(newInfoA.causes.length).toBe(0); expect(infoLinkAB.source).toBeUndefined(); @@ -218,7 +218,7 @@ describe('MinKemlExample', () => { expect(infoLinkCA.source).toBeDefined(); expect(infoLinkBA.target).toBeDefined(); expect(infoLinkCA.target).toBeDefined(); - newInfoA.destruct(DeletionMode.RELAXED); + newInfoA.destruct(DeletionAdditionMode.RELAXED); expect(newInfoA.source).toBeUndefined(); expect(newInfoA.targetedBy.length).toBe(0); expect(infoLinkBA.source).toBeDefined(); @@ -241,7 +241,7 @@ describe('MinKemlExample', () => { expect(infoLinkCA.source).toBeDefined(); expect(infoLinkBA.target).toBeDefined(); expect(infoLinkCA.target).toBeDefined(); - newInfoA.destruct(DeletionMode.CASCADE); + newInfoA.destruct(DeletionAdditionMode.STRICT); expect(newInfoA.source).toBeUndefined(); expect(newInfoA.targetedBy.length).toBe(0); expect(infoLinkBA.source).toBeUndefined(); @@ -249,4 +249,21 @@ describe('MinKemlExample', () => { expect(infoLinkBA.target).toBeUndefined(); expect(infoLinkCA.target).toBeUndefined(); }); + + it('should consistently remove and add referencable objects', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); + sendMessA.addUsage(newInfoA); + sendMessA.addUsage(newInfoB); + expect(sendMessA.uses.length).toBe(2) + expect(newInfoA.isUsedOn.length).toBe(1); + expect(newInfoB.isUsedOn.length).toBe(1); + newInfoA.removeIsUsedOn(sendMessA); + expect(sendMessA.uses.length).toBe(1); + expect(newInfoA.isUsedOn.length).toBe(0); + expect(newInfoB.isUsedOn.length).toBe(1); + }); }); \ No newline at end of file diff --git a/projects/emfular/src/lib/referencing/test/min-keml-example.ts b/projects/emfular/src/lib/referencing/test/conversation-example.ts similarity index 96% rename from projects/emfular/src/lib/referencing/test/min-keml-example.ts rename to projects/emfular/src/lib/referencing/test/conversation-example.ts index 58c204b..7c2a33a 100644 --- a/projects/emfular/src/lib/referencing/test/min-keml-example.ts +++ b/projects/emfular/src/lib/referencing/test/conversation-example.ts @@ -1,4 +1,4 @@ -import { DeletionMode } from "../../utils/deletion-mode"; +import { DeletionAdditionMode } from "../../utils/deletion-addition-mode"; import { ReLinkListContainer } from "../referencable/container/link/re-link-list-container"; import { ReLinkSingleContainer } from "../referencable/container/link/re-link-single-container"; import { ReTreeListContainer } from "../referencable/container/tree/re-tree-list-container"; @@ -181,7 +181,7 @@ export class SendMessage extends Message { addUsage(info: Information) { this._uses.add(info) } - removeUsage(info: Information, mode: DeletionMode = DeletionMode.RELAXED): boolean { + removeUsage(info: Information, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED): boolean { return this._uses.remove(info, mode) } @@ -222,7 +222,7 @@ export class ReceiveMessage extends Message { addRepetition(info: Information) { this._repeats.add(info); } - removeRepetition(info: Information, mode: DeletionMode = DeletionMode.RELAXED): boolean { + removeRepetition(info: Information, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED): boolean { return this._repeats.remove(info, mode); } @@ -286,7 +286,7 @@ export abstract class Information< addIsUsedOn(...send: SendMessage[]){ send.map(s => this._isUsedOn.add(s)) } - removeIsUsedOn(send: SendMessage, mode: DeletionMode = DeletionMode.RELAXED){ + removeIsUsedOn(send: SendMessage, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED){ this._isUsedOn.remove(send, mode) } @@ -298,7 +298,7 @@ export abstract class Information< addRepeatedBy(msg: ReceiveMessage) { this._repeatedBy.add(msg) } - removeRepeatedBy(msg: ReceiveMessage, mode: DeletionMode = DeletionMode.RELAXED) { + removeRepeatedBy(msg: ReceiveMessage, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED) { this._repeatedBy.remove(msg, mode) } From 5bc1be031b75c6ad8b94d14cfb0649ccac5fa987 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Wed, 18 Mar 2026 14:40:50 +0100 Subject: [PATCH 15/22] fixed small mistakes after resolving merge conflicts --- .../link/re-link-list-container.spec.ts | 5 +- .../container/link/re-link-list-container.ts | 2 +- .../link/re-link-single-container.spec.ts | 2 +- .../referencable/container/re-container.ts | 2 +- .../container/re-list-container.ts | 9 +- .../container/re-single-container.ts | 4 +- .../shallow/re-derived-list-container.spec.ts | 2 +- .../shallow/re-derived-list-container.ts | 3 +- .../re-derived-single-container.spec.ts | 2 +- .../shallow/re-derived-single-container.ts | 5 +- .../test/conversation-example.spec.ts | 269 ----------- .../referencing/test/conversation-example.ts | 434 ------------------ .../referencing/test/referencable-tester.ts | 6 +- .../test/referencables-with-children.spec.ts | 35 +- .../test/referencables-with-children.ts | 1 - 15 files changed, 23 insertions(+), 758 deletions(-) delete mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.spec.ts delete mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.ts diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts index 45d46cb..802ca90 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts @@ -1,7 +1,6 @@ import { ReLinkListContainer } from './re-link-list-container'; import {ReferencableTester, refTesterRef} from "../../../test/referencable-tester"; import {RootWithChildren, ReChild3} from "../../../test/referencables-with-children"; -import { DeletionMode } from '../../../../utils/deletion-mode'; describe('ReLinkListContainer', () => { it('should create an instance', () => { @@ -12,11 +11,11 @@ describe('ReLinkListContainer', () => { it("should give true if the remove and remove inverse chain triggered an element removal", () => { let tester = new RootWithChildren() let elem2 = new ReChild3() - expect(tester.link3.remove(elem2, DeletionMode.RELAXED)).toBeFalse() + expect(tester.link3.remove(elem2)).toBeFalse() tester.link3.push(elem2) expect(tester.link3.length).toBe(1) expect(tester.link3).toContain(elem2) - expect(tester.link3.remove(elem2, DeletionMode.RELAXED)).toBeTrue() + expect(tester.link3.remove(elem2)).toBeTrue() expect(tester.link3.length).toBe(0) }) }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts index 207fd04..649d051 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts @@ -14,7 +14,7 @@ export class ReLinkListContainer< implements ReLinkContainer { constructor(parent: P, name: string, refMeta: ReferenceMeta, isRequired: boolean) { - super(parent, name, refMeta); + super(parent, name, refMeta, isRequired); this._parent.$otherReferences.push(this) } diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts index 49cb250..dd9c0ad 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReLinkSingleContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReLinkSingleContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); + expect(new ReLinkSingleContainer(tester, 'refName', refTesterRef.references.test, false)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index 24bee0d..c61095b 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -14,7 +14,7 @@ export abstract class ReContainer< readonly inverseName?: string; readonly isRequired: boolean; - protected constructor(parent: P, referenceName: string, isRequired: boolean, refMeta: ReferenceMeta) { + protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { this._parent = parent; this.meta = refMeta; this.referenceName = referenceName; diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts index 36d1f22..d3323ad 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts @@ -5,6 +5,7 @@ import {ModelList} from "./hide/model-list"; import {createListProxy} from "./hide/list-proxy"; import {ReListInterface} from "./re-list-interface"; import {ReferenceMeta} from "../../../binding/model-definition"; +import {DeletionMode} from "../../../utils/deletion-mode"; export abstract class ReListContainer< @@ -17,8 +18,8 @@ implements ReListInterface{ private _proxy?: ModelList; - protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) { - super(parent, referenceName, refMeta); + protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { + super(parent, referenceName, refMeta, isRequired); } override get(): T[] { @@ -32,8 +33,8 @@ implements ReListInterface{ return this._proxy; } - override delete() { - ListUpdater.destructAllFromChangingList(this._instance) + override delete(mode: DeletionMode) { + ListUpdater.destructAllFromChangingList(this._instance, mode) } move(from: number, to: number) { diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts index cea350e..3498077 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts @@ -11,8 +11,8 @@ implements ReSingleInterface{ protected _instance?: T ; - protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta ) { - super(parent, referenceName, refMeta); + protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { + super(parent, referenceName, refMeta, isRequired); } override get(): T | undefined { diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts index a90f96b..8f9be1a 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReDerivedListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReDerivedListContainer(tester, () => [],'refName', refTesterRef.references.test)).toBeTruthy(); + expect(new ReDerivedListContainer(tester, () => [],'refName', refTesterRef.references.test, false)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts index 9632db0..34a3a89 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts @@ -23,8 +23,9 @@ export class ReDerivedListContainer< computeOrSymbol: ((owner: P) => T[]) | symbol, referenceName: string, refMeta: ReferenceMeta, + isRequired: boolean ) { - super(parent, referenceName, refMeta); + super(parent, referenceName, refMeta, isRequired); this.resolver = new ReDerivationResolver(computeOrSymbol); } diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts index 93537bc..10dc2c1 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReDerivedSingleContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReDerivedSingleContainer(tester, () => {},'refName', refTesterRef.references.test)).toBeTruthy(); + expect(new ReDerivedSingleContainer(tester, () => {},'refName', refTesterRef.references.test, false)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts index f0e81c1..9683500 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts @@ -17,9 +17,10 @@ export class ReDerivedSingleContainer< parent: P, computeOrSymbol: ((owner: P) => T | undefined) | symbol, referenceName: string, - refMeta: ReferenceMeta + refMeta: ReferenceMeta, + isRequired: boolean ) { - super(parent, referenceName, refMeta); + super(parent, referenceName, refMeta, isRequired); this.resolver = new ReDerivationResolver(computeOrSymbol); } diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts deleted file mode 100644 index 60497d8..0000000 --- a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts +++ /dev/null @@ -1,269 +0,0 @@ -import {DeletionAdditionMode} from "../../utils/deletion-addition-mode"; -import { - Author, - ConversationPartner, - InformationLink, - InformationLinkType, - NewInformation, - ReceiveMessage, - SendMessage -} from "./conversation-example"; - -describe('MinKemlExample', () => { - it('relaxed deletion of NewInformation should delete reference to source', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - newInfoA.destruct(DeletionAdditionMode.RELAXED); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - }); - - it('cascaded deletion of NewInformation should delete reference to source', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - newInfoA.destruct(DeletionAdditionMode.STRICT); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - }); - - it('relaxed deletion of NewInformation should delete reference to isUsedOn', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); - let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); - let author: Author = Author.create('Author'); - author.addMessage(sendMessA, sendMessB); - sendMessA.addUsage(newInfoA); - sendMessA.addUsage(newInfoB); - sendMessB.addUsage(newInfoA); - sendMessB.addUsage(newInfoB); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(newInfoA.isUsedOn.length).toBe(2); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(2); - expect(sendMessB.uses.length).toBe(2); - newInfoA.destruct(DeletionAdditionMode.RELAXED); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(newInfoA.isUsedOn.length).toBe(0); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(1); - expect(sendMessB.uses.length).toBe(1); - }); - - it('cascaded deletion of NewInformation should delete reference to isUsedOn', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); - let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); - let author: Author = Author.create('Author'); - author.addMessage(sendMessA, sendMessB); - sendMessA.addUsage(newInfoA); - sendMessA.addUsage(newInfoB); - sendMessB.addUsage(newInfoA); - sendMessB.addUsage(newInfoB); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(newInfoA.isUsedOn.length).toBe(2); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(2); - expect(sendMessB.uses.length).toBe(2); - newInfoA.destruct(DeletionAdditionMode.STRICT); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(newInfoA.isUsedOn.length).toBe(0); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(1); - expect(sendMessB.uses.length).toBe(1); - }); - - it('relaxed deletion of NewInformation should delete reference to repeatedBy', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); - let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - recMessRepA.addRepetition(newInfoA); - recMessRepA.addRepetition(newInfoB); - recMessRepB.addRepetition(newInfoA); - recMessRepB.addRepetition(newInfoB); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(recMessRepA.repeats.length).toBe(2); - expect(recMessRepB.repeats.length).toBe(2); - expect(newInfoA.repeatedBy.length).toBe(2); - expect(newInfoB.repeatedBy.length).toBe(2); - newInfoA.destruct(DeletionAdditionMode.RELAXED); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(recMessRepA.repeats.length).toBe(1); - expect(recMessRepB.repeats.length).toBe(1); - expect(newInfoA.repeatedBy.length).toBe(0); - expect(newInfoB.repeatedBy.length).toBe(2); - }); - - it('cascaded deletion of NewInformation should delete reference to repeatedBy', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); - let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - recMessRepA.addRepetition(newInfoA); - recMessRepA.addRepetition(newInfoB); - recMessRepB.addRepetition(newInfoA); - recMessRepB.addRepetition(newInfoB); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(recMessRepA.repeats.length).toBe(2); - expect(recMessRepB.repeats.length).toBe(2); - expect(newInfoA.repeatedBy.length).toBe(2); - expect(newInfoB.repeatedBy.length).toBe(2); - newInfoA.destruct(DeletionAdditionMode.STRICT); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(recMessRepA.repeats.length).toBe(1); - expect(recMessRepB.repeats.length).toBe(1); - expect(newInfoA.repeatedBy.length).toBe(0); - expect(newInfoB.repeatedBy.length).toBe(2); - }); - - it('relaxed deletion of NewInformation should delete reference to causes but not delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); - let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.causes.length).toBe(2); - expect(infoLinkAB.source).toBeDefined(); - expect(infoLinkAC.source).toBeDefined(); - expect(infoLinkAB.target).toBeDefined(); - expect(infoLinkAC.target).toBeDefined(); - newInfoA.destruct(DeletionAdditionMode.RELAXED); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.causes.length).toBe(0); - expect(infoLinkAB.source).toBeUndefined(); - expect(infoLinkAC.source).toBeUndefined(); - expect(infoLinkAB.target).toBeDefined(); - expect(infoLinkAC.target).toBeDefined(); - }); - - it('cascaded deletion of NewInformation should delete reference to causes and delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); - let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.causes.length).toBe(2); - expect(infoLinkAB.source).toBeDefined(); - expect(infoLinkAC.source).toBeDefined(); - expect(infoLinkAB.target).toBeDefined(); - expect(infoLinkAC.target).toBeDefined(); - newInfoA.destruct(DeletionAdditionMode.STRICT); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.causes.length).toBe(0); - expect(infoLinkAB.source).toBeUndefined(); - expect(infoLinkAC.source).toBeUndefined(); - expect(infoLinkAB.target).toBeUndefined(); - expect(infoLinkAC.target).toBeUndefined(); - }); - - it('relaxed deletion of NewInformation should delete reference to targetedBy but not delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); - let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.targetedBy.length).toBe(2); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeDefined(); - expect(infoLinkCA.target).toBeDefined(); - newInfoA.destruct(DeletionAdditionMode.RELAXED); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.targetedBy.length).toBe(0); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeUndefined(); - expect(infoLinkCA.target).toBeUndefined(); - }); - - it('cascaded deletion of NewInformation should delete reference to targetedBy and delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); - let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.targetedBy.length).toBe(2); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeDefined(); - expect(infoLinkCA.target).toBeDefined(); - newInfoA.destruct(DeletionAdditionMode.STRICT); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.targetedBy.length).toBe(0); - expect(infoLinkBA.source).toBeUndefined(); - expect(infoLinkCA.source).toBeUndefined(); - expect(infoLinkBA.target).toBeUndefined(); - expect(infoLinkCA.target).toBeUndefined(); - }); - - it('should consistently remove and add referencable objects', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); - sendMessA.addUsage(newInfoA); - sendMessA.addUsage(newInfoB); - expect(sendMessA.uses.length).toBe(2) - expect(newInfoA.isUsedOn.length).toBe(1); - expect(newInfoB.isUsedOn.length).toBe(1); - newInfoA.removeIsUsedOn(sendMessA); - expect(sendMessA.uses.length).toBe(1); - expect(newInfoA.isUsedOn.length).toBe(0); - expect(newInfoB.isUsedOn.length).toBe(1); - }); -}); \ No newline at end of file diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.ts b/projects/emfular/src/lib/referencing/test/conversation-example.ts deleted file mode 100644 index 7c2a33a..0000000 --- a/projects/emfular/src/lib/referencing/test/conversation-example.ts +++ /dev/null @@ -1,434 +0,0 @@ -import { DeletionAdditionMode } from "../../utils/deletion-addition-mode"; -import { ReLinkListContainer } from "../referencable/container/link/re-link-list-container"; -import { ReLinkSingleContainer } from "../referencable/container/link/re-link-single-container"; -import { ReTreeListContainer } from "../referencable/container/tree/re-tree-list-container"; -import { ReTreeParentContainer } from "../referencable/container/tree/re-tree-parent-container"; -import { ReTreeSingleContainer } from "../referencable/container/tree/re-tree-single-container"; -import { Referencable } from "../referencable/referenceable"; - -export enum InformationLinkType { - SUPPLEMENT = 'SUPPLEMENT', - SUPPORT = 'SUPPORT', - STRONG_SUPPORT = 'STRONG_SUPPORT', - ATTACK = 'ATTACK', - STRONG_ATTACK = 'STRONG_ATTACK', -} - -export class Conversation extends Referencable { - static readonly $authorName = 'author'; - static readonly $conversationPartnersName = 'conversationPartners'; - - title: string; - - _author: ReTreeSingleContainer; - get author(): Author { - return this._author.get()!! - } - set author(author: Author) { - this._author.add(author); - } - _conversationPartners: ReTreeListContainer; - get conversationPartners(): ConversationPartner[] { - return this._conversationPartners.get() - } - addCP(...cps: ConversationPartner[]) { - cps.map(cp => { - this._conversationPartners.add(cp) - }) - } - - constructor( - title: string = 'New Conversation' - ) { - super(); - this._author = new ReTreeSingleContainer(this, Conversation.$authorName, false); - this._conversationPartners = new ReTreeListContainer(this, Conversation.$conversationPartnersName, false); - this.title = title; - this.author = new Author(); - } - - static create(title: string = 'New conversation', author?: Author): Conversation { - const conv = new Conversation('New Conversation'); - conv.title = title; - conv.author = author? author: new Author(); - return conv; - } - -} - -export abstract class LifeLine extends Referencable{ - name: string; - xPosition: number; //int todo - - protected constructor(name?: string, xPosition: number = 0) { - super(); - this.name = name? name: ''; - this.xPosition = xPosition; - } - -} - -export class ConversationPartner extends LifeLine { - - constructor(name: string = 'NewPartner', xPosition?: number) { - super(name, xPosition); - } - - static create(name: string = 'NewPartner', xPosition?: number): ConversationPartner { - const cp = new ConversationPartner() - cp.name = name - cp.xPosition = xPosition? xPosition : 0; - return cp; - } - -} - -export class Author extends LifeLine{ - static readonly $preknowledgeName: string = 'preknowledge'; - static readonly $messagesName: string = 'messages'; - - _preknowledge: ReTreeListContainer; - get preknowledge(): Preknowledge[] { - return this._preknowledge.get() - } - addPreknowledge(...preknowledge: Preknowledge[]) { - preknowledge.map(p => { - this._preknowledge.add(p) - }) - } - - _messages: ReTreeListContainer; - get messages(): Message[] { - return this._messages.get() - } - addMessage(...msgs: Message[]) { - msgs.map(m => { - this._messages.add(m) - }) - } - - constructor() { - super(); - this._preknowledge = new ReTreeListContainer(this, Author.$preknowledgeName, false) - this._messages = new ReTreeListContainer(this, Author.$messagesName, false) - } - - static create(name?: string, xPosition: number = 0): Author { - const auth = new Author() - auth.name = name? name: '' - auth.xPosition = xPosition - return auth - } - -} - -export abstract class Message extends Referencable { - public static readonly $counterPartName = 'counterPart' - - _counterPart: ReLinkSingleContainer; - get counterPart(): ConversationPartner { - return this._counterPart.get()!! //todo - } - set counterPart(value: ConversationPartner) { - this._counterPart.add(value); - } - - timing: number; - content: string; - originalContent?: string; - - protected constructor( - timing: number = 0, - content: string = "", - originalContent?: string, - ) { - super(); - this.timing = timing; - this.content = content; - this.originalContent = originalContent; - this._counterPart = new ReLinkSingleContainer(this, Message.$counterPartName) - } - - static isSend(eClass: string) { - return eClass.endsWith("SendMessage"); - } - - isSend(): this is SendMessage { - return this instanceof SendMessage - } - - isReceive(): this is ReceiveMessage { - return this instanceof ReceiveMessage - } - - static newMessage(isSend: boolean, counterPart: ConversationPartner, timing: number, content: string, originalContent: string = 'Original content'): Message { - if (isSend) { - return SendMessage.create(counterPart, timing, content, originalContent) - } else { - return ReceiveMessage.create(counterPart, timing, content, originalContent) - } - } -} - - -export class SendMessage extends Message { - public static readonly $usesName = 'uses' - - private readonly _uses: ReLinkListContainer; - get uses(): Information[] { - return this._uses.get(); - } - addUsage(info: Information) { - this._uses.add(info) - } - removeUsage(info: Information, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED): boolean { - return this._uses.remove(info, mode) - } - - constructor( - timing?: number, - content: string = 'New send content', - originalContent?: string, - ) { - super(timing, content, originalContent); - this._uses = new ReLinkListContainer(this, SendMessage.$usesName, false, Information.$isUsedOnName); - } - - static create(counterPart: ConversationPartner, - timing: number, - content: string = 'New send content', - originalContent?: string, - ): SendMessage { - const send = new SendMessage(timing, content, originalContent); - send.counterPart = counterPart; - return send; - } - -} - -export class ReceiveMessage extends Message { - static readonly $generatesName: string = 'generates'; - static readonly $repeatsName: string = 'repeats'; - - _generates: ReTreeListContainer; - get generates(): NewInformation[] { - return this._generates.get()!! - } - - _repeats: ReLinkListContainer; - get repeats(): Information[] { - return this._repeats.get(); - } - addRepetition(info: Information) { - this._repeats.add(info); - } - removeRepetition(info: Information, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED): boolean { - return this._repeats.remove(info, mode); - } - - isInterrupted: boolean = false; - - constructor( - timing?: number, - content: string = "New receive content", - originalContent?: string, - isInterrupted: boolean = false, - ) { - super(timing, content, originalContent); - this._generates = new ReTreeListContainer(this, ReceiveMessage.$generatesName, false, NewInformation.$sourceName); - this._repeats = new ReLinkListContainer(this, ReceiveMessage.$repeatsName, false, Information.$repeatedByName); - this.isInterrupted = isInterrupted; - } - - static create(counterPart: ConversationPartner, - timing: number, - content?: string, - originalContent?: string, - isInterrupted: boolean = false,): ReceiveMessage { - const rec = new ReceiveMessage(timing, content, originalContent, isInterrupted); - rec.counterPart = counterPart; - return rec - } - -} - -export abstract class Information< - P extends Referencable=Referencable -> extends Referencable

{ - - message: string = ""; - isInstruction: boolean = false; - initialTrust: number | undefined; - currentTrust: number | undefined; - feltTrustImmediately: number | undefined; - feltTrustAfterwards: number | undefined; - - abstract getTiming(): number; - - static readonly $causesName: string = 'causes' - static readonly $isUsedOnName: string = 'isUsedOn' - static readonly $repeatedByName: string = 'repeatedBy' - static readonly $targetedByName: string = 'targetedBy' - readonly _causes: ReTreeListContainer; - get causes(): InformationLink[] { - return this._causes.get(); - } - - readonly _targetedBy: ReLinkListContainer - get targetedBy(): InformationLink[] { - return this._targetedBy.get(); - } - - readonly _isUsedOn: ReLinkListContainer - get isUsedOn(): SendMessage[] { - return this._isUsedOn.get(); - } - addIsUsedOn(...send: SendMessage[]){ - send.map(s => this._isUsedOn.add(s)) - } - removeIsUsedOn(send: SendMessage, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED){ - this._isUsedOn.remove(send, mode) - } - - readonly _repeatedBy: ReLinkListContainer - get repeatedBy(): ReceiveMessage[] { - return this._repeatedBy.get(); - } - - addRepeatedBy(msg: ReceiveMessage) { - this._repeatedBy.add(msg) - } - removeRepeatedBy(msg: ReceiveMessage, mode: DeletionAdditionMode = DeletionAdditionMode.RELAXED) { - this._repeatedBy.remove(msg, mode) - } - - protected constructor() { - super(); - - this._causes = new ReTreeListContainer(this, NewInformation.$causesName, false, InformationLink.$sourceName); - this._targetedBy = new ReLinkListContainer(this, Information.$targetedByName, false, InformationLink.$targetName) - this._isUsedOn = new ReLinkListContainer(this, 'isUsedOn', false, 'uses'); - this._repeatedBy = new ReLinkListContainer(this, NewInformation.$repeatedByName, false, ReceiveMessage.$repeatsName); - } - - abstract duplicate(): Information; - -} -export class NewInformation extends Information { - - public static readonly $sourceName = 'source' - - readonly _source: ReTreeParentContainer; - set source(rec: ReceiveMessage) { - this._source.add(rec) - } - get source(): ReceiveMessage { - return this._source.get()!! - } - - override getTiming(): number { - return this.source.timing - } - - constructor() { - super(); - this._source = new ReTreeParentContainer(this, NewInformation.$sourceName, false, ReceiveMessage.$generatesName); - } - - override duplicate(): NewInformation { - return NewInformation.create(this.source, 'Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); - } - - static create(source: ReceiveMessage, - message: string, isInstruction: boolean = false, - initialTrust?: number, currentTrust?: number, feltTrustImmediately?: number, feltTrustAfterwards?: number,): NewInformation { - const info = new NewInformation(); - info.source = source; - info.message = message; - info.isInstruction = isInstruction; - info.initialTrust = initialTrust; - info.currentTrust = currentTrust; - info.feltTrustImmediately = feltTrustImmediately; - info.feltTrustAfterwards = feltTrustAfterwards; - return info; - } - -} - -export class Preknowledge extends Information { - - constructor() { - super(); - } - - getTiming(): number { - let timing; - if (this.isUsedOn?.length >0) { - timing = Math.min(...this.isUsedOn.map(send => send.timing)); - } else { - timing = 0 - } - return timing - } - - override duplicate(): Preknowledge { - return Preknowledge.create('Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); - } - - static create(message: string = 'Preknowledge', isInstruction: boolean = false, - initialTrust?: number, currentTrust?: number, - feltTrustImmediately?: number, feltTrustAfterwards?: number): Preknowledge { - const pre = new Preknowledge() - pre.message = message - pre.isInstruction = isInstruction - pre.initialTrust = initialTrust - pre.currentTrust = currentTrust - pre.feltTrustImmediately = feltTrustImmediately - pre.feltTrustAfterwards = feltTrustAfterwards - return pre - } - -} - -export class InformationLink extends Referencable { - - public static readonly $sourceName = 'source' - public static readonly $targetName = 'target' - readonly _source: ReTreeParentContainer - get source(): Information { - return this._source.get()!!; //todo - } - set source(source: Information) { - this._source.add(source) - } - - readonly _target: ReLinkSingleContainer - get target(): Information { - return this._target.get()!!; - } - set target(target: Information) { - this._target.add(target); - } - - type: InformationLinkType = InformationLinkType.SUPPLEMENT; - linkText?: string; - - constructor() { - super(); - this._source = new ReTreeParentContainer(this, InformationLink.$sourceName, false, NewInformation.$causesName); - this._target = new ReLinkSingleContainer(this, InformationLink.$targetName, true, Information.$targetedByName); - } - - static create(source: Information, target: Information, type: InformationLinkType, linkText?: string,): InformationLink { - const link = new InformationLink() - link.source = source - link.target = target; - link.type = type - link.linkText = linkText - return link - } - -} - - - diff --git a/projects/emfular/src/lib/referencing/test/referencable-tester.ts b/projects/emfular/src/lib/referencing/test/referencable-tester.ts index 0526134..f00f45d 100644 --- a/projects/emfular/src/lib/referencing/test/referencable-tester.ts +++ b/projects/emfular/src/lib/referencing/test/referencable-tester.ts @@ -12,10 +12,10 @@ export const refTesterRef = { } export const modelDef= { - name: "", prefix: "", uri: "", + name: "", prefix: "", uri: "", classes: { - ReferencableTester: refTesterRef - } + ReferencableTester: refTesterRef + } } as const satisfies ModelDefinition; @eClass(modelDef, "ReferencableTester") diff --git a/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts b/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts index 709a7f2..9b7120e 100644 --- a/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts +++ b/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts @@ -6,7 +6,6 @@ import { } from "./referencables-with-children"; import {SerializationContext} from "../../serialization/serialization-context"; import {RefHandler} from "../ref/ref-handler"; -import {Ref} from "../ref/ref"; describe('ReContainersWithListChild tests', () => { @@ -69,6 +68,7 @@ describe('ReContainersWithListChild tests', () => { expect(r2_1.child3.length).toBe(0); expect(r3_1.parentPointer).toEqual(undefined) expect(r3_2.parentPointer).toEqual(r2_2) + }); it ('should serialize a Referencable1WithChildren correctly', () => { @@ -103,9 +103,6 @@ describe('ReContainersWithListChild tests', () => { }] } expect(r1.toJson()).toEqual(r1json) - const json: RootWithChildrenJson = r1.toJson() - let refs: Ref[] = json.link3 as Ref[] - expect(refs.length).toBe(1) }) //todo deserialization test @@ -121,34 +118,4 @@ describe('ReContainersWithListChild tests', () => { expect(r1.$otherReferences.length).toBe(1) }) - it('should allow swapping elements in a ModelList created via decorators', () => { - const r = new RootWithChildren(); - const m1 = new Middle2WithChildren(); - const m2 = new Middle2WithChildren(); - const m3 = new Middle2WithChildren(); - - r.child2.push(m1, m2, m3); - expect(r.child2.map(x => x)).toEqual([m1, m2, m3]); - - (r.child2 as any).swap(0, 2); - - expect(r.child2.map(x => x)).toEqual([m3, m2, m1]); - }); - - - it('should allow flatMap on ModelList proxies (shows proxy behaves like array)', () => { - // Build a small containment structure - r1.child2.push(r2_1, r2_2); - - r2_1.child3.push(r3_1); - r2_2.child3.push(r3_2); - - // This is the critical line that used to fail: - const allChildren = r1.child2.flatMap(m => m.child3); - - expect(allChildren.length).toBe(2); - expect(allChildren).toContain(r3_1); - expect(allChildren).toContain(r3_2); - }); - }); \ No newline at end of file diff --git a/projects/emfular/src/lib/referencing/test/referencables-with-children.ts b/projects/emfular/src/lib/referencing/test/referencables-with-children.ts index 2769cdd..cf04ce9 100644 --- a/projects/emfular/src/lib/referencing/test/referencables-with-children.ts +++ b/projects/emfular/src/lib/referencing/test/referencables-with-children.ts @@ -5,7 +5,6 @@ import {JsonOf} from "../../serialization/json-deserializable"; import {ModelDefinition} from "../../binding/model-definition"; import {reference} from "../../binding/reference-decorator"; import {ModelList} from "../referencable/container/hide/model-list"; -import { DeletionMode } from "../../utils/deletion-mode"; export const ModelWithChildren: ModelDefinition = { name: "namespace", From 57f0529ed3e2b9a31b0a9d3c66b48c2d908b668c Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Mon, 23 Mar 2026 15:43:42 +0100 Subject: [PATCH 16/22] removed isRequired from constructors and set it depending on meta.min, made DeletionMode argument optional with default mode RELAXED --- .../container/link/re-link-container.ts | 2 +- .../link/re-link-list-container.spec.ts | 2 +- .../container/link/re-link-list-container.ts | 12 +++--- .../link/re-link-single-container.spec.ts | 2 +- .../link/re-link-single-container.ts | 10 ++--- .../referencable/container/re-container.ts | 10 ++--- .../container/re-list-container.ts | 6 +-- .../container/re-single-container.ts | 4 +- .../shallow/re-derived-list-container.spec.ts | 2 +- .../shallow/re-derived-list-container.ts | 5 +-- .../re-derived-single-container.spec.ts | 2 +- .../shallow/re-derived-single-container.ts | 5 +-- .../shallow/re-tree-parent-container.spec.ts | 2 +- .../shallow/re-tree-parent-container.ts | 6 +-- .../tree/re-tree-list-container.spec.ts | 2 +- .../container/tree/re-tree-list-container.ts | 10 ++--- .../tree/re-tree-single-container.spec.ts | 2 +- .../tree/re-tree-single-container.ts | 6 +-- .../referencing/referencable/referenceable.ts | 6 +-- .../test/referencables-with-children.spec.ts | 41 +++++++++++++++++-- 20 files changed, 85 insertions(+), 52 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts index b7c0b0f..929b656 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-container.ts @@ -14,7 +14,7 @@ export interface ReLinkContainer< this._parent.$otherReferences.push(this) }*/ - removeFromInverse(item: T, mode: DeletionMode): boolean; + removeFromInverse(item: T, mode?: DeletionMode): boolean; toJson(ctx: SerializationContext): Ref[] | Ref | undefined } diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts index 802ca90..4cfb65b 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts @@ -5,7 +5,7 @@ import {RootWithChildren, ReChild3} from "../../../test/referencables-with-child describe('ReLinkListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReLinkListContainer(tester, 'refName', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReLinkListContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); it("should give true if the remove and remove inverse chain triggered an element removal", () => { diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts index 649d051..161515b 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.ts @@ -5,7 +5,7 @@ import {ReLinkContainer} from "./re-link-container"; import {ListUpdater} from "../../../../utils/list-updater"; import {ReListContainer} from "../re-list-container"; import {ReferenceMeta} from "../../../../binding/model-definition"; -import { DeletionMode } from "../../../../utils/deletion-mode"; +import {DeletionMode} from "../../../../utils/deletion-mode"; export class ReLinkListContainer< T extends Referencable, @@ -13,8 +13,8 @@ export class ReLinkListContainer< > extends ReListContainer implements ReLinkContainer { - constructor(parent: P, name: string, refMeta: ReferenceMeta, isRequired: boolean) { - super(parent, name, refMeta, isRequired); + constructor(parent: P, name: string, refMeta: ReferenceMeta) { + super(parent, name, refMeta); this._parent.$otherReferences.push(this) } @@ -34,7 +34,7 @@ implements ReLinkContainer { return this._instance.map(i => ctx.get(i)) } - override remove(item: T, mode: DeletionMode): boolean { + override remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { const res = ListUpdater.removeFromList(item, this._instance) if (res) { if(this.inverseName !== undefined) { @@ -44,11 +44,11 @@ implements ReLinkContainer { return res; //todo behaviour of flag different to add?? } - override delete(mode: DeletionMode) { + override delete(mode: DeletionMode = DeletionMode.RELAXED) { ListUpdater.destructAllFromChangingList(this._instance, mode) } - removeFromInverse(item: T, mode: DeletionMode): boolean { + removeFromInverse(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { if(this.inverseName !== undefined) { for (const child of [...this._instance]) { child.removeFromReferencableContainer(this.inverseName, item, mode) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts index dd9c0ad..66872b5 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReLinkSingleContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReLinkSingleContainer(tester, 'refName', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReLinkSingleContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts index dd095c4..786220f 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts @@ -12,8 +12,8 @@ export class ReLinkSingleContainer< > extends ReSingleContainer implements ReLinkContainer { - constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { - super(parent, referenceName, refMeta, isRequired); + constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) { + super(parent, referenceName, refMeta); this._parent.$otherReferences.push(this) } @@ -36,7 +36,7 @@ implements ReLinkContainer { } } - remove(item: T, mode: DeletionMode): boolean { + remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { if(this._instance == item) { if (this.inverseName != undefined) { item.removeFromReferencableContainer(this.inverseName, this._parent, mode) @@ -48,11 +48,11 @@ implements ReLinkContainer { } } - override delete(mode: DeletionMode) { + override delete(mode: DeletionMode = DeletionMode.RELAXED) { this._instance?.destruct(mode) } - removeFromInverse(item: T, mode: DeletionMode): boolean { + removeFromInverse(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { if(this.inverseName !== undefined) { this._instance?.removeFromReferencableContainer(this.inverseName, item, mode) return true; // todo refine? diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts index c61095b..5cb5d5d 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-container.ts @@ -2,7 +2,7 @@ import {Referencable} from "../referenceable"; import {SerializationContext} from "../../../serialization/serialization-context"; import {ReferenceMeta} from "../../../binding/model-definition"; import {ModelRegistry} from "../../../binding/model-registry"; -import { DeletionMode } from "../../../utils/deletion-mode"; +import {DeletionMode} from "../../../utils/deletion-mode"; export abstract class ReContainer< T extends Referencable, @@ -14,11 +14,11 @@ export abstract class ReContainer< readonly inverseName?: string; readonly isRequired: boolean; - protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { + protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) { this._parent = parent; this.meta = refMeta; this.referenceName = referenceName; - this.isRequired = isRequired; + this.isRequired = this.meta.min != undefined && this.meta.min > 0; this.inverseName = refMeta.opposite; } @@ -46,10 +46,10 @@ export abstract class ReContainer< return this.isAcceptableItem( new srcConstr()) //todo } - abstract remove(item: T, mode: DeletionMode): boolean; + abstract remove(item: T, mode?: DeletionMode): boolean; //called to destruct all elements in the container (e.g. when destroying a parent - abstract delete(mode: DeletionMode): void + abstract delete(mode?: DeletionMode): void abstract toJson(ctx: SerializationContext): any } diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts index d3323ad..fe52bef 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-list-container.ts @@ -18,8 +18,8 @@ implements ReListInterface{ private _proxy?: ModelList; - protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { - super(parent, referenceName, refMeta, isRequired); + protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) { + super(parent, referenceName, refMeta); } override get(): T[] { @@ -33,7 +33,7 @@ implements ReListInterface{ return this._proxy; } - override delete(mode: DeletionMode) { + override delete(mode: DeletionMode = DeletionMode.RELAXED) { ListUpdater.destructAllFromChangingList(this._instance, mode) } diff --git a/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts index 3498077..cb1e542 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/re-single-container.ts @@ -11,8 +11,8 @@ implements ReSingleInterface{ protected _instance?: T ; - protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { - super(parent, referenceName, refMeta, isRequired); + protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) { + super(parent, referenceName, refMeta); } override get(): T | undefined { diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts index 8f9be1a..a90f96b 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReDerivedListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReDerivedListContainer(tester, () => [],'refName', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReDerivedListContainer(tester, () => [],'refName', refTesterRef.references.test)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts index 34a3a89..c8d76c0 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-list-container.ts @@ -22,10 +22,9 @@ export class ReDerivedListContainer< parent: P, computeOrSymbol: ((owner: P) => T[]) | symbol, referenceName: string, - refMeta: ReferenceMeta, - isRequired: boolean + refMeta: ReferenceMeta ) { - super(parent, referenceName, refMeta, isRequired); + super(parent, referenceName, refMeta); this.resolver = new ReDerivationResolver(computeOrSymbol); } diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts index 10dc2c1..93537bc 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReDerivedSingleContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReDerivedSingleContainer(tester, () => {},'refName', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReDerivedSingleContainer(tester, () => {},'refName', refTesterRef.references.test)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts index 9683500..f0e81c1 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-derived-single-container.ts @@ -17,10 +17,9 @@ export class ReDerivedSingleContainer< parent: P, computeOrSymbol: ((owner: P) => T | undefined) | symbol, referenceName: string, - refMeta: ReferenceMeta, - isRequired: boolean + refMeta: ReferenceMeta ) { - super(parent, referenceName, refMeta, isRequired); + super(parent, referenceName, refMeta); this.resolver = new ReDerivationResolver(computeOrSymbol); } diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.spec.ts index a111aae..4ebd6e4 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.spec.ts @@ -4,7 +4,7 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReferencableTreeParentContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReTreeParentContainer(tester, 'refName', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReTreeParentContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); it('should serialize a contained ref', () => { diff --git a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.ts b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.ts index 68251c1..db64e8b 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/shallow/re-tree-parent-container.ts @@ -11,8 +11,8 @@ export class ReTreeParentContainer> implements ReSingleInterface, ReShallowInterface{ - constructor(parent: T, referenceName: string, refMeta: ReferenceMeta, isRequired: boolean) { - super(parent, referenceName, refMeta, isRequired); // referenceName is actually unused for this container type + constructor(parent: T, referenceName: string, refMeta: ReferenceMeta) { + super(parent, referenceName, refMeta); // referenceName is actually unused for this container type } get(): T["ParentType"] | undefined { @@ -29,7 +29,7 @@ implements ReSingleInterface, return item.addToReferencableContainer(this.inverseName, me) } - remove(item: T["ParentType"], mode: DeletionMode): boolean { + remove(item: T["ParentType"], mode: DeletionMode = DeletionMode.RELAXED): boolean { return item.removeFromReferencableContainer(this.inverseName, this._parent, mode) } diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts index 7781cb0..911d218 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReferencableTreeListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReTreeListContainer(tester, 'refName', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReTreeListContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts index 45078ef..9a1abf7 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts @@ -15,8 +15,8 @@ implements ReTreeChildrenContainer { readonly defaultEClass?: string; - constructor(parent: T["ParentType"], name: string, refMeta: ReferenceMeta, isRequired: boolean, eClass?: string) { - super(parent, name, refMeta, isRequired); + constructor(parent: T["ParentType"], name: string, refMeta: ReferenceMeta, eClass?: string) { + super(parent, name, refMeta); this.defaultEClass = eClass; this._parent.$treeChildren.push(this) } @@ -41,12 +41,12 @@ implements ReTreeChildrenContainer { return false; } else { item.setParent(this); - oldParent?.remove(item, DeletionMode.RELAXED) + oldParent?.remove(item) return ListUpdater.addToListIfMissing(item, this._instance) } } - override remove(item: T, mode: DeletionMode): boolean { + override remove(item: T): boolean { let removed = ListUpdater.removeFromList(item, this._instance) if(removed){ item.setParent(undefined); @@ -55,7 +55,7 @@ implements ReTreeChildrenContainer { return false; } - override delete(mode: DeletionMode) { + override delete(mode: DeletionMode = DeletionMode.RELAXED) { ListUpdater.destructAllFromChangingList(this._instance, mode) } diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts index e2c3e10..5fd42bf 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts @@ -4,6 +4,6 @@ import {ReferencableTester, refTesterRef} from "../../../test/referencable-teste describe('ReferencableTreeSingletonContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() - expect(new ReTreeSingleContainer(tester, 'test', refTesterRef.references.test, false)).toBeTruthy(); + expect(new ReTreeSingleContainer(tester, 'test', refTesterRef.references.test)).toBeTruthy(); }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts index a15e900..58404c9 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts @@ -14,8 +14,8 @@ implements ReTreeChildrenContainer { readonly defaultEClass?: string; - constructor(parent: T["ParentType"], referenceName: string, refMeta: ReferenceMeta, isRequired: boolean, eClass?: string) { - super(parent, referenceName, refMeta, isRequired); + constructor(parent: T["ParentType"], referenceName: string, refMeta: ReferenceMeta, eClass?: string) { + super(parent, referenceName, refMeta); this.defaultEClass = eClass; this._parent.$treeChildren.push(this) } @@ -47,7 +47,7 @@ implements ReTreeChildrenContainer { return false; } - delete(mode: DeletionMode) { + delete(mode: DeletionMode = DeletionMode.RELAXED) { if (mode === DeletionMode.CASCADE) { this._instance?.destruct(mode) } else if (mode === DeletionMode.RELAXED) { diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index 1700b3d..9dd82bb 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -50,7 +50,7 @@ export abstract class Referencable< setParent(parent: ReTreeChildrenContainer | undefined) { if(this.$parent) { - this.$parent.remove(this, DeletionMode.RELAXED) + this.$parent.remove(this) } this.$parent = parent; } @@ -75,7 +75,7 @@ export abstract class Referencable< } } - destruct(mode: DeletionMode) { + destruct(mode: DeletionMode = DeletionMode.RELAXED) { this.$parent?.remove(this, mode) this.$otherReferences.forEach(refContainer => { refContainer.removeFromInverse(this, mode) @@ -116,7 +116,7 @@ export abstract class Referencable< return this.getContainer(name).add(item) } - public removeFromReferencableContainer>(name: string, item: T, mode: DeletionMode): boolean { + public removeFromReferencableContainer>(name: string, item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { let container = this.getContainer(name) let result = container.remove(item, mode) if (result && mode == DeletionMode.CASCADE && container.isRequired) { diff --git a/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts b/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts index 9b7120e..f855dcf 100644 --- a/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts +++ b/projects/emfular/src/lib/referencing/test/referencables-with-children.spec.ts @@ -1,11 +1,15 @@ import { EClasses, - RootWithChildren, RootWithChildrenJson, - Middle2WithChildren, Middle2WithChildrenJson, - ReChild3, ReChild3Json + Middle2WithChildren, + Middle2WithChildrenJson, + ReChild3, + ReChild3Json, + RootWithChildren, + RootWithChildrenJson } from "./referencables-with-children"; import {SerializationContext} from "../../serialization/serialization-context"; import {RefHandler} from "../ref/ref-handler"; +import {Ref} from "../ref/ref"; describe('ReContainersWithListChild tests', () => { @@ -103,6 +107,9 @@ describe('ReContainersWithListChild tests', () => { }] } expect(r1.toJson()).toEqual(r1json) + const json: RootWithChildrenJson = r1.toJson() + let refs: Ref[] = json.link3 as Ref[] + expect(refs.length).toBe(1) }) //todo deserialization test @@ -118,4 +125,32 @@ describe('ReContainersWithListChild tests', () => { expect(r1.$otherReferences.length).toBe(1) }) + it('should allow swapping elements in a ModelList created via decorators', () => { + const r = new RootWithChildren(); + const m1 = new Middle2WithChildren(); + const m2 = new Middle2WithChildren(); + const m3 = new Middle2WithChildren(); + + r.child2.push(m1, m2, m3); + expect(r.child2.map(x => x)).toEqual([m1, m2, m3]); + + (r.child2 as any).swap(0, 2); + + expect(r.child2.map(x => x)).toEqual([m3, m2, m1]); + }); + + it('should allow flatMap on ModelList proxies (shows proxy behaves like array)', () => { + // Build a small containment structure + r1.child2.push(r2_1, r2_2); + + r2_1.child3.push(r3_1); + r2_2.child3.push(r3_2); + + // This is the critical line that used to fail: + const allChildren = r1.child2.flatMap(m => m.child3); + + expect(allChildren.length).toBe(2); + expect(allChildren).toContain(r3_1); + expect(allChildren).toContain(r3_2); + }); }); \ No newline at end of file From 3cfd611bfb2317c326adb9039746df070beeb170 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Mon, 23 Mar 2026 15:52:28 +0100 Subject: [PATCH 17/22] deleted old incompatible tests --- .../test/conversation-example.spec.ts | 131 ------ .../referencing/test/conversation-example.ts | 433 ------------------ 2 files changed, 564 deletions(-) delete mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.spec.ts delete mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.ts diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts deleted file mode 100644 index d9721e7..0000000 --- a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Author, - ConversationPartner, - InformationLink, - InformationLinkType, - NewInformation, - ReceiveMessage, - SendMessage -} from "./conversation-example"; - -describe('Conversation Example', () => { - it('deletion of NewInformation should delete reference to source', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - }); - - it('deletion of NewInformation should delete reference to isUsedOn', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); - let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); - let author: Author = Author.create('Author'); - author.addMessage(sendMessA, sendMessB); - sendMessA.addUsage(newInfoA); - sendMessA.addUsage(newInfoB); - sendMessB.addUsage(newInfoA); - sendMessB.addUsage(newInfoB); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(newInfoA.isUsedOn.length).toBe(2); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(2); - expect(sendMessB.uses.length).toBe(2); - newInfoA.destruct(); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(newInfoA.isUsedOn.length).toBe(0); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(1); - expect(sendMessB.uses.length).toBe(1); - }); - - it('deletion of NewInformation should delete reference to repeatedBy', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); - let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - recMessRepA.addRepetition(newInfoA); - recMessRepA.addRepetition(newInfoB); - recMessRepB.addRepetition(newInfoA); - recMessRepB.addRepetition(newInfoB); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(recMessRepA.repeats.length).toBe(2); - expect(recMessRepB.repeats.length).toBe(2); - expect(newInfoA.repeatedBy.length).toBe(2); - expect(newInfoB.repeatedBy.length).toBe(2); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(recMessRepA.repeats.length).toBe(1); - expect(recMessRepB.repeats.length).toBe(1); - expect(newInfoA.repeatedBy.length).toBe(0); - expect(newInfoB.repeatedBy.length).toBe(2); - }); - - it('deletion of NewInformation should delete reference to causes and delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); - let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.causes.length).toBe(2); - expect(infoLinkAB.source).toBeDefined(); - expect(infoLinkAC.source).toBeDefined(); - expect(infoLinkAB.target).toBeDefined(); - expect(infoLinkAC.target).toBeDefined(); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.causes.length).toBe(0); - expect(infoLinkAB.source).toBeUndefined(); - expect(infoLinkAC.source).toBeUndefined(); - expect(infoLinkAB.target).toBeUndefined(); - expect(infoLinkAC.target).toBeUndefined(); - }); - - it('deletion of NewInformation should delete reference to targetedBy and delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); - let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.targetedBy.length).toBe(2); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeDefined(); - expect(infoLinkCA.target).toBeDefined(); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.targetedBy.length).toBe(0); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeUndefined(); - expect(infoLinkCA.target).toBeUndefined(); - }); -}); diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.ts b/projects/emfular/src/lib/referencing/test/conversation-example.ts deleted file mode 100644 index ba9d2cc..0000000 --- a/projects/emfular/src/lib/referencing/test/conversation-example.ts +++ /dev/null @@ -1,433 +0,0 @@ -import { ReLinkListContainer } from "../referencable/container/link/re-link-list-container"; -import { ReLinkSingleContainer } from "../referencable/container/link/re-link-single-container"; -import { ReTreeListContainer } from "../referencable/container/tree/re-tree-list-container"; -import { ReTreeParentContainer } from "../referencable/container/tree/re-tree-parent-container"; -import { ReTreeSingleContainer } from "../referencable/container/tree/re-tree-single-container"; -import { Referencable } from "../referencable/referenceable"; - -export enum InformationLinkType { - SUPPLEMENT = 'SUPPLEMENT', - SUPPORT = 'SUPPORT', - STRONG_SUPPORT = 'STRONG_SUPPORT', - ATTACK = 'ATTACK', - STRONG_ATTACK = 'STRONG_ATTACK', -} - -export class Conversation extends Referencable { - static readonly $authorName = 'author'; - static readonly $conversationPartnersName = 'conversationPartners'; - - title: string; - - _author: ReTreeSingleContainer; - get author(): Author { - return this._author.get()!! - } - set author(author: Author) { - this._author.add(author); - } - _conversationPartners: ReTreeListContainer; - get conversationPartners(): ConversationPartner[] { - return this._conversationPartners.get() - } - addCP(...cps: ConversationPartner[]) { - cps.map(cp => { - this._conversationPartners.add(cp) - }) - } - - constructor( - title: string = 'New Conversation' - ) { - super(); - this._author = new ReTreeSingleContainer(this, Conversation.$authorName); - this._conversationPartners = new ReTreeListContainer(this, Conversation.$conversationPartnersName); - this.title = title; - this.author = new Author(); - } - - static create(title: string = 'New conversation', author?: Author): Conversation { - const conv = new Conversation('New Conversation'); - conv.title = title; - conv.author = author? author: new Author(); - return conv; - } - -} - -export abstract class LifeLine extends Referencable{ - name: string; - xPosition: number; //int todo - - protected constructor(name?: string, xPosition: number = 0) { - super(); - this.name = name? name: ''; - this.xPosition = xPosition; - } - -} - -export class ConversationPartner extends LifeLine { - - constructor(name: string = 'NewPartner', xPosition?: number) { - super(name, xPosition); - } - - static create(name: string = 'NewPartner', xPosition?: number): ConversationPartner { - const cp = new ConversationPartner() - cp.name = name - cp.xPosition = xPosition? xPosition : 0; - return cp; - } - -} - -export class Author extends LifeLine{ - static readonly $preknowledgeName: string = 'preknowledge'; - static readonly $messagesName: string = 'messages'; - - _preknowledge: ReTreeListContainer; - get preknowledge(): Preknowledge[] { - return this._preknowledge.get() - } - addPreknowledge(...preknowledge: Preknowledge[]) { - preknowledge.map(p => { - this._preknowledge.add(p) - }) - } - - _messages: ReTreeListContainer; - get messages(): Message[] { - return this._messages.get() - } - addMessage(...msgs: Message[]) { - msgs.map(m => { - this._messages.add(m) - }) - } - - constructor() { - super(); - this._preknowledge = new ReTreeListContainer(this, Author.$preknowledgeName) - this._messages = new ReTreeListContainer(this, Author.$messagesName) - } - - static create(name?: string, xPosition: number = 0): Author { - const auth = new Author() - auth.name = name? name: '' - auth.xPosition = xPosition - return auth - } - -} - -export abstract class Message extends Referencable { - public static readonly $counterPartName = 'counterPart' - - _counterPart: ReLinkSingleContainer; - get counterPart(): ConversationPartner { - return this._counterPart.get()!! //todo - } - set counterPart(value: ConversationPartner) { - this._counterPart.add(value); - } - - timing: number; - content: string; - originalContent?: string; - - protected constructor( - timing: number = 0, - content: string = "", - originalContent?: string, - ) { - super(); - this.timing = timing; - this.content = content; - this.originalContent = originalContent; - this._counterPart = new ReLinkSingleContainer(this, Message.$counterPartName) - } - - static isSend(eClass: string) { - return eClass.endsWith("SendMessage"); - } - - isSend(): this is SendMessage { - return this instanceof SendMessage - } - - isReceive(): this is ReceiveMessage { - return this instanceof ReceiveMessage - } - - static newMessage(isSend: boolean, counterPart: ConversationPartner, timing: number, content: string, originalContent: string = 'Original content'): Message { - if (isSend) { - return SendMessage.create(counterPart, timing, content, originalContent) - } else { - return ReceiveMessage.create(counterPart, timing, content, originalContent) - } - } -} - - -export class SendMessage extends Message { - public static readonly $usesName = 'uses' - - private readonly _uses: ReLinkListContainer; - get uses(): Information[] { - return this._uses.get(); - } - addUsage(info: Information) { - this._uses.add(info) - } - removeUsage(info: Information): boolean { - return this._uses.remove(info) - } - - constructor( - timing?: number, - content: string = 'New send content', - originalContent?: string, - ) { - super(timing, content, originalContent); - this._uses = new ReLinkListContainer(this, SendMessage.$usesName, Information.$isUsedOnName); - } - - static create(counterPart: ConversationPartner, - timing: number, - content: string = 'New send content', - originalContent?: string, - ): SendMessage { - const send = new SendMessage(timing, content, originalContent); - send.counterPart = counterPart; - return send; - } - -} - -export class ReceiveMessage extends Message { - static readonly $generatesName: string = 'generates'; - static readonly $repeatsName: string = 'repeats'; - - _generates: ReTreeListContainer; - get generates(): NewInformation[] { - return this._generates.get()!! - } - - _repeats: ReLinkListContainer; - get repeats(): Information[] { - return this._repeats.get(); - } - addRepetition(info: Information) { - this._repeats.add(info); - } - removeRepetition(info: Information): boolean { - return this._repeats.remove(info); - } - - isInterrupted: boolean = false; - - constructor( - timing?: number, - content: string = "New receive content", - originalContent?: string, - isInterrupted: boolean = false, - ) { - super(timing, content, originalContent); - this._generates = new ReTreeListContainer(this, ReceiveMessage.$generatesName, NewInformation.$sourceName); - this._repeats = new ReLinkListContainer(this, ReceiveMessage.$repeatsName, Information.$repeatedByName); - this.isInterrupted = isInterrupted; - } - - static create(counterPart: ConversationPartner, - timing: number, - content?: string, - originalContent?: string, - isInterrupted: boolean = false,): ReceiveMessage { - const rec = new ReceiveMessage(timing, content, originalContent, isInterrupted); - rec.counterPart = counterPart; - return rec - } - -} - -export abstract class Information< - P extends Referencable=Referencable -> extends Referencable

{ - - message: string = ""; - isInstruction: boolean = false; - initialTrust: number | undefined; - currentTrust: number | undefined; - feltTrustImmediately: number | undefined; - feltTrustAfterwards: number | undefined; - - abstract getTiming(): number; - - static readonly $causesName: string = 'causes' - static readonly $isUsedOnName: string = 'isUsedOn' - static readonly $repeatedByName: string = 'repeatedBy' - static readonly $targetedByName: string = 'targetedBy' - readonly _causes: ReTreeListContainer; - get causes(): InformationLink[] { - return this._causes.get(); - } - - readonly _targetedBy: ReLinkListContainer - get targetedBy(): InformationLink[] { - return this._targetedBy.get(); - } - - readonly _isUsedOn: ReLinkListContainer - get isUsedOn(): SendMessage[] { - return this._isUsedOn.get(); - } - addIsUsedOn(...send: SendMessage[]){ - send.map(s => this._isUsedOn.add(s)) - } - removeIsUsedOn(send: SendMessage){ - this._isUsedOn.remove(send) - } - - readonly _repeatedBy: ReLinkListContainer - get repeatedBy(): ReceiveMessage[] { - return this._repeatedBy.get(); - } - - addRepeatedBy(msg: ReceiveMessage) { - this._repeatedBy.add(msg) - } - removeRepeatedBy(msg: ReceiveMessage) { - this._repeatedBy.remove(msg) - } - - protected constructor() { - super(); - - this._causes = new ReTreeListContainer(this, NewInformation.$causesName, InformationLink.$sourceName); - this._targetedBy = new ReLinkListContainer(this, Information.$targetedByName, InformationLink.$targetName) - this._isUsedOn = new ReLinkListContainer(this, 'isUsedOn', 'uses'); - this._repeatedBy = new ReLinkListContainer(this, NewInformation.$repeatedByName, ReceiveMessage.$repeatsName); - } - - abstract duplicate(): Information; - -} -export class NewInformation extends Information { - - public static readonly $sourceName = 'source' - - readonly _source: ReTreeParentContainer; - set source(rec: ReceiveMessage) { - this._source.add(rec) - } - get source(): ReceiveMessage { - return this._source.get()!! - } - - override getTiming(): number { - return this.source.timing - } - - constructor() { - super(); - this._source = new ReTreeParentContainer(this, NewInformation.$sourceName, ReceiveMessage.$generatesName); - } - - override duplicate(): NewInformation { - return NewInformation.create(this.source, 'Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); - } - - static create(source: ReceiveMessage, - message: string, isInstruction: boolean = false, - initialTrust?: number, currentTrust?: number, feltTrustImmediately?: number, feltTrustAfterwards?: number,): NewInformation { - const info = new NewInformation(); - info.source = source; - info.message = message; - info.isInstruction = isInstruction; - info.initialTrust = initialTrust; - info.currentTrust = currentTrust; - info.feltTrustImmediately = feltTrustImmediately; - info.feltTrustAfterwards = feltTrustAfterwards; - return info; - } - -} - -export class Preknowledge extends Information { - - constructor() { - super(); - } - - getTiming(): number { - let timing; - if (this.isUsedOn?.length >0) { - timing = Math.min(...this.isUsedOn.map(send => send.timing)); - } else { - timing = 0 - } - return timing - } - - override duplicate(): Preknowledge { - return Preknowledge.create('Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); - } - - static create(message: string = 'Preknowledge', isInstruction: boolean = false, - initialTrust?: number, currentTrust?: number, - feltTrustImmediately?: number, feltTrustAfterwards?: number): Preknowledge { - const pre = new Preknowledge() - pre.message = message - pre.isInstruction = isInstruction - pre.initialTrust = initialTrust - pre.currentTrust = currentTrust - pre.feltTrustImmediately = feltTrustImmediately - pre.feltTrustAfterwards = feltTrustAfterwards - return pre - } - -} - -export class InformationLink extends Referencable { - - public static readonly $sourceName = 'source' - public static readonly $targetName = 'target' - readonly _source: ReTreeParentContainer - get source(): Information { - return this._source.get()!!; //todo - } - set source(source: Information) { - this._source.add(source) - } - - readonly _target: ReLinkSingleContainer - get target(): Information { - return this._target.get()!!; - } - set target(target: Information) { - this._target.add(target); - } - - type: InformationLinkType = InformationLinkType.SUPPLEMENT; - linkText?: string; - - constructor() { - super(); - this._source = new ReTreeParentContainer(this, InformationLink.$sourceName, NewInformation.$causesName); - this._target = new ReLinkSingleContainer(this, InformationLink.$targetName, Information.$targetedByName); - } - - static create(source: Information, target: Information, type: InformationLinkType, linkText?: string,): InformationLink { - const link = new InformationLink() - link.source = source - link.target = target; - link.type = type - link.linkText = linkText - return link - } - -} - - - From 1abcb3fe6d11ea7189031e890cb4bd1639c34b60 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Mon, 23 Mar 2026 19:03:28 +0100 Subject: [PATCH 18/22] added deleted tests again in incompatible state --- .../test/conversation-example.spec.ts | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.spec.ts diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts new file mode 100644 index 0000000..e485dc4 --- /dev/null +++ b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts @@ -0,0 +1,131 @@ +import { + Author, + ConversationPartner, + InformationLink, + InformationLinkType, + NewInformation, + ReceiveMessage, + SendMessage +} from "./conversation-example"; + +describe('Conversation Example', () => { + it('deletion of NewInformation should delete reference to source', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + newInfoA.destruct(); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + }); + + it('deletion of NewInformation should delete reference to isUsedOn', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); + let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); + let author: Author = Author.create('Author'); + author.addMessage(sendMessA, sendMessB); + sendMessA.addUsage(newInfoA); + sendMessA.addUsage(newInfoB); + sendMessB.addUsage(newInfoA); + sendMessB.addUsage(newInfoB); + expect(author.messages.length).toBe(2); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + expect(newInfoA.isUsedOn.length).toBe(2); + expect(newInfoB.isUsedOn.length).toBe(2); + expect(sendMessA.uses.length).toBe(2); + expect(sendMessB.uses.length).toBe(2); + newInfoA.destruct(); + expect(author.messages.length).toBe(2); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + expect(newInfoA.isUsedOn.length).toBe(0); + expect(newInfoB.isUsedOn.length).toBe(2); + expect(sendMessA.uses.length).toBe(1); + expect(sendMessB.uses.length).toBe(1); + }); + + it('deletion of NewInformation should delete reference to repeatedBy', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); + let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + recMessRepA.addRepetition(newInfoA); + recMessRepA.addRepetition(newInfoB); + recMessRepB.addRepetition(newInfoA); + recMessRepB.addRepetition(newInfoB); + expect(newInfoA.source).toBeDefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(2); + expect(recMessRepA.repeats.length).toBe(2); + expect(recMessRepB.repeats.length).toBe(2); + expect(newInfoA.repeatedBy.length).toBe(2); + expect(newInfoB.repeatedBy.length).toBe(2); + newInfoA.destruct(); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoB.source).toBeDefined(); + expect(recMess.generates.length).toBe(1); + expect(recMessRepA.repeats.length).toBe(1); + expect(recMessRepB.repeats.length).toBe(1); + expect(newInfoA.repeatedBy.length).toBe(0); + expect(newInfoB.repeatedBy.length).toBe(2); + }); + + it('deletion of NewInformation should delete reference to causes and delete children', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); + let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); + let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); + expect(newInfoA.source).toBeDefined(); + expect(newInfoA.causes.length).toBe(2); + expect(infoLinkAB.source).toBeDefined(); + expect(infoLinkAC.source).toBeDefined(); + expect(infoLinkAB.target).toBeDefined(); + expect(infoLinkAC.target).toBeDefined(); + newInfoA.destruct(); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoA.causes.length).toBe(0); + expect(infoLinkAB.source).toBeUndefined(); + expect(infoLinkAC.source).toBeUndefined(); + expect(infoLinkAB.target).toBeUndefined(); + expect(infoLinkAC.target).toBeUndefined(); + }); + + it('deletion of NewInformation should delete reference to targetedBy and delete children', () => { + let partner: ConversationPartner = new ConversationPartner(); + let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); + let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); + let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); + let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); + let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); + let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); + expect(newInfoA.source).toBeDefined(); + expect(newInfoA.targetedBy.length).toBe(2); + expect(infoLinkBA.source).toBeDefined(); + expect(infoLinkCA.source).toBeDefined(); + expect(infoLinkBA.target).toBeDefined(); + expect(infoLinkCA.target).toBeDefined(); + newInfoA.destruct(); + expect(newInfoA.source).toBeUndefined(); + expect(newInfoA.targetedBy.length).toBe(0); + expect(infoLinkBA.source).toBeDefined(); + expect(infoLinkCA.source).toBeDefined(); + expect(infoLinkBA.target).toBeUndefined(); + expect(infoLinkCA.target).toBeUndefined(); + }); +}); \ No newline at end of file From 1a9dcc9a59a65b9c3ca908fac34f9f95a7e6d247 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Mon, 23 Mar 2026 19:07:19 +0100 Subject: [PATCH 19/22] added deleted classes for tests again in incompatible state --- .../referencing/test/conversation-example.ts | 430 ++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.ts diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.ts b/projects/emfular/src/lib/referencing/test/conversation-example.ts new file mode 100644 index 0000000..bf2501a --- /dev/null +++ b/projects/emfular/src/lib/referencing/test/conversation-example.ts @@ -0,0 +1,430 @@ +import { ReLinkListContainer } from "../referencable/container/link/re-link-list-container"; +import { ReLinkSingleContainer } from "../referencable/container/link/re-link-single-container"; +import { ReTreeListContainer } from "../referencable/container/tree/re-tree-list-container"; +import { ReTreeParentContainer } from "../referencable/container/shallow/re-tree-parent-container"; +import { ReTreeSingleContainer } from "../referencable/container/tree/re-tree-single-container"; +import { Referencable } from "../referencable/referenceable"; + +export enum InformationLinkType { + SUPPLEMENT = 'SUPPLEMENT', + SUPPORT = 'SUPPORT', + STRONG_SUPPORT = 'STRONG_SUPPORT', + ATTACK = 'ATTACK', + STRONG_ATTACK = 'STRONG_ATTACK', +} + +export class Conversation extends Referencable { + static readonly $authorName = 'author'; + static readonly $conversationPartnersName = 'conversationPartners'; + + title: string; + + _author: ReTreeSingleContainer; + get author(): Author { + return this._author.get()!! + } + set author(author: Author) { + this._author.add(author); + } + _conversationPartners: ReTreeListContainer; + get conversationPartners(): ConversationPartner[] { + return this._conversationPartners.get() + } + addCP(...cps: ConversationPartner[]) { + cps.map(cp => { + this._conversationPartners.add(cp) + }) + } + + constructor( + title: string = 'New Conversation' + ) { + super(); + this._author = new ReTreeSingleContainer(this, Conversation.$authorName); + this._conversationPartners = new ReTreeListContainer(this, Conversation.$conversationPartnersName); + this.title = title; + this.author = new Author(); + } + + static create(title: string = 'New conversation', author?: Author): Conversation { + const conv = new Conversation('New Conversation'); + conv.title = title; + conv.author = author? author: new Author(); + return conv; + } + +} + +export abstract class LifeLine extends Referencable{ + name: string; + xPosition: number; //int + + protected constructor(name?: string, xPosition: number = 0) { + super(); + this.name = name? name: ''; + this.xPosition = xPosition; + } + +} + +export class ConversationPartner extends LifeLine { + + constructor(name: string = 'NewPartner', xPosition?: number) { + super(name, xPosition); + } + + static create(name: string = 'NewPartner', xPosition?: number): ConversationPartner { + const cp = new ConversationPartner() + cp.name = name + cp.xPosition = xPosition? xPosition : 0; + return cp; + } + +} + +export class Author extends LifeLine{ + static readonly $preknowledgeName: string = 'preknowledge'; + static readonly $messagesName: string = 'messages'; + + _preknowledge: ReTreeListContainer; + get preknowledge(): Preknowledge[] { + return this._preknowledge.get() + } + addPreknowledge(...preknowledge: Preknowledge[]) { + preknowledge.map(p => { + this._preknowledge.add(p) + }) + } + + _messages: ReTreeListContainer; + get messages(): Message[] { + return this._messages.get() + } + addMessage(...msgs: Message[]) { + msgs.map(m => { + this._messages.add(m) + }) + } + + constructor() { + super(); + this._preknowledge = new ReTreeListContainer(this, Author.$preknowledgeName) + this._messages = new ReTreeListContainer(this, Author.$messagesName) + } + + static create(name?: string, xPosition: number = 0): Author { + const auth = new Author() + auth.name = name? name: '' + auth.xPosition = xPosition + return auth + } + +} + +export abstract class Message extends Referencable { + public static readonly $counterPartName = 'counterPart' + + _counterPart: ReLinkSingleContainer; + get counterPart(): ConversationPartner { + return this._counterPart.get()!! + } + set counterPart(value: ConversationPartner) { + this._counterPart.add(value); + } + + timing: number; + content: string; + originalContent?: string; + + protected constructor( + timing: number = 0, + content: string = "", + originalContent?: string, + ) { + super(); + this.timing = timing; + this.content = content; + this.originalContent = originalContent; + this._counterPart = new ReLinkSingleContainer(this, Message.$counterPartName) + } + + static isSend(eClass: string) { + return eClass.endsWith("SendMessage"); + } + + isSend(): this is SendMessage { + return this instanceof SendMessage + } + + isReceive(): this is ReceiveMessage { + return this instanceof ReceiveMessage + } + + static newMessage(isSend: boolean, counterPart: ConversationPartner, timing: number, content: string, originalContent: string = 'Original content'): Message { + if (isSend) { + return SendMessage.create(counterPart, timing, content, originalContent) + } else { + return ReceiveMessage.create(counterPart, timing, content, originalContent) + } + } +} + + +export class SendMessage extends Message { + public static readonly $usesName = 'uses' + + private readonly _uses: ReLinkListContainer; + get uses(): Information[] { + return this._uses.get(); + } + addUsage(info: Information) { + this._uses.add(info) + } + removeUsage(info: Information): boolean { + return this._uses.remove(info) + } + + constructor( + timing?: number, + content: string = 'New send content', + originalContent?: string, + ) { + super(timing, content, originalContent); + this._uses = new ReLinkListContainer(this, SendMessage.$usesName, Information.$isUsedOnName); + } + + static create(counterPart: ConversationPartner, + timing: number, + content: string = 'New send content', + originalContent?: string, + ): SendMessage { + const send = new SendMessage(timing, content, originalContent); + send.counterPart = counterPart; + return send; + } + +} + +export class ReceiveMessage extends Message { + static readonly $generatesName: string = 'generates'; + static readonly $repeatsName: string = 'repeats'; + + _generates: ReTreeListContainer; + get generates(): NewInformation[] { + return this._generates.get()!! + } + + _repeats: ReLinkListContainer; + get repeats(): Information[] { + return this._repeats.get(); + } + addRepetition(info: Information) { + this._repeats.add(info); + } + removeRepetition(info: Information): boolean { + return this._repeats.remove(info); + } + + isInterrupted: boolean = false; + + constructor( + timing?: number, + content: string = "New receive content", + originalContent?: string, + isInterrupted: boolean = false, + ) { + super(timing, content, originalContent); + this._generates = new ReTreeListContainer(this, ReceiveMessage.$generatesName, NewInformation.$sourceName); + this._repeats = new ReLinkListContainer(this, ReceiveMessage.$repeatsName, Information.$repeatedByName); + this.isInterrupted = isInterrupted; + } + + static create(counterPart: ConversationPartner, + timing: number, + content?: string, + originalContent?: string, + isInterrupted: boolean = false,): ReceiveMessage { + const rec = new ReceiveMessage(timing, content, originalContent, isInterrupted); + rec.counterPart = counterPart; + return rec + } + +} + +export abstract class Information< + P extends Referencable=Referencable +> extends Referencable

{ + + message: string = ""; + isInstruction: boolean = false; + initialTrust: number | undefined; + currentTrust: number | undefined; + feltTrustImmediately: number | undefined; + feltTrustAfterwards: number | undefined; + + abstract getTiming(): number; + + static readonly $causesName: string = 'causes' + static readonly $isUsedOnName: string = 'isUsedOn' + static readonly $repeatedByName: string = 'repeatedBy' + static readonly $targetedByName: string = 'targetedBy' + readonly _causes: ReTreeListContainer; + get causes(): InformationLink[] { + return this._causes.get(); + } + + readonly _targetedBy: ReLinkListContainer + get targetedBy(): InformationLink[] { + return this._targetedBy.get(); + } + + readonly _isUsedOn: ReLinkListContainer + get isUsedOn(): SendMessage[] { + return this._isUsedOn.get(); + } + addIsUsedOn(...send: SendMessage[]){ + send.map(s => this._isUsedOn.add(s)) + } + removeIsUsedOn(send: SendMessage){ + this._isUsedOn.remove(send) + } + + readonly _repeatedBy: ReLinkListContainer + get repeatedBy(): ReceiveMessage[] { + return this._repeatedBy.get(); + } + + addRepeatedBy(msg: ReceiveMessage) { + this._repeatedBy.add(msg) + } + removeRepeatedBy(msg: ReceiveMessage) { + this._repeatedBy.remove(msg) + } + + protected constructor() { + super(); + + this._causes = new ReTreeListContainer(this, NewInformation.$causesName, InformationLink.$sourceName); + this._targetedBy = new ReLinkListContainer(this, Information.$targetedByName, InformationLink.$targetName) + this._isUsedOn = new ReLinkListContainer(this, 'isUsedOn', 'uses'); + this._repeatedBy = new ReLinkListContainer(this, NewInformation.$repeatedByName, ReceiveMessage.$repeatsName); + } + + abstract duplicate(): Information; + +} +export class NewInformation extends Information { + + public static readonly $sourceName = 'source' + + readonly _source: ReTreeParentContainer; + set source(rec: ReceiveMessage) { + this._source.add(rec) + } + get source(): ReceiveMessage { + return this._source.get()!! + } + + override getTiming(): number { + return this.source.timing + } + + constructor() { + super(); + this._source = new ReTreeParentContainer(this, NewInformation.$sourceName, ReceiveMessage.$generatesName); + } + + override duplicate(): NewInformation { + return NewInformation.create(this.source, 'Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); + } + + static create(source: ReceiveMessage, + message: string, isInstruction: boolean = false, + initialTrust?: number, currentTrust?: number, feltTrustImmediately?: number, feltTrustAfterwards?: number,): NewInformation { + const info = new NewInformation(); + info.source = source; + info.message = message; + info.isInstruction = isInstruction; + info.initialTrust = initialTrust; + info.currentTrust = currentTrust; + info.feltTrustImmediately = feltTrustImmediately; + info.feltTrustAfterwards = feltTrustAfterwards; + return info; + } + +} + +export class Preknowledge extends Information { + + constructor() { + super(); + } + + getTiming(): number { + let timing; + if (this.isUsedOn?.length >0) { + timing = Math.min(...this.isUsedOn.map(send => send.timing)); + } else { + timing = 0 + } + return timing + } + + override duplicate(): Preknowledge { + return Preknowledge.create('Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); + } + + static create(message: string = 'Preknowledge', isInstruction: boolean = false, + initialTrust?: number, currentTrust?: number, + feltTrustImmediately?: number, feltTrustAfterwards?: number): Preknowledge { + const pre = new Preknowledge() + pre.message = message + pre.isInstruction = isInstruction + pre.initialTrust = initialTrust + pre.currentTrust = currentTrust + pre.feltTrustImmediately = feltTrustImmediately + pre.feltTrustAfterwards = feltTrustAfterwards + return pre + } + +} + +export class InformationLink extends Referencable { + + public static readonly $sourceName = 'source' + public static readonly $targetName = 'target' + readonly _source: ReTreeParentContainer + get source(): Information { + return this._source.get()!!; + } + set source(source: Information) { + this._source.add(source) + } + + readonly _target: ReLinkSingleContainer + get target(): Information { + return this._target.get()!!; + } + set target(target: Information) { + this._target.add(target); + } + + type: InformationLinkType = InformationLinkType.SUPPLEMENT; + linkText?: string; + + constructor() { + super(); + this._source = new ReTreeParentContainer(this, InformationLink.$sourceName, NewInformation.$causesName); + this._target = new ReLinkSingleContainer(this, InformationLink.$targetName, Information.$targetedByName); + } + + static create(source: Information, target: Information, type: InformationLinkType, linkText?: string,): InformationLink { + const link = new InformationLink() + link.source = source + link.target = target; + link.type = type + link.linkText = linkText + return link + } + +} \ No newline at end of file From 972a8e826658a1b96108db78abe64e5f679f1d27 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Sun, 29 Mar 2026 23:42:54 +0200 Subject: [PATCH 20/22] deleted conversation test classes for future replacement and updated list-proxy deletion methods --- .../referencable/container/hide/list-proxy.ts | 18 +- .../test/conversation-example.spec.ts | 131 ------ .../referencing/test/conversation-example.ts | 430 ------------------ 3 files changed, 12 insertions(+), 567 deletions(-) delete mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.spec.ts delete mode 100644 projects/emfular/src/lib/referencing/test/conversation-example.ts diff --git a/projects/emfular/src/lib/referencing/referencable/container/hide/list-proxy.ts b/projects/emfular/src/lib/referencing/referencable/container/hide/list-proxy.ts index 791f1a5..4040b29 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/hide/list-proxy.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/hide/list-proxy.ts @@ -1,6 +1,7 @@ import {Referencable} from "../../referenceable"; import {ModelList} from "./model-list"; import {ReListInterface} from "../re-list-interface"; +import {DeletionMode} from "../../../../utils/deletion-mode"; export function createListProxy< T extends Referencable, @@ -103,20 +104,25 @@ export function createListProxy< items.some(item => container.remove(item)); } + if (prop === "removeCascade") { + return (...items: T[]) => + items.some(item => container.remove(item, DeletionMode.CASCADE)) + } + if(prop === "delete") { - return () => { - container.delete(); + return (mode?: DeletionMode) => { + container.delete(mode); } } // The pop() method removes the last element from an array and returns that value to the caller. // If you call pop() on an empty array, it returns undefined. if (prop === "pop") { - return () => { + return (mode?: DeletionMode) => { const arr = container.get(); if (arr.length === 0) return undefined; const last = arr[arr.length - 1]; - container.remove(last); + container.remove(last, mode); return last; }; } @@ -124,11 +130,11 @@ export function createListProxy< // The shift() method of Array instances removes the first element from an array and returns that removed element. // If you call shift() on an empty array, it returns undefined. if (prop === "shift") { - return () => { + return (mode?: DeletionMode) => { const arr = container.get(); if (arr.length === 0) return undefined; const first = arr[0]; - container.remove(first); + container.remove(first, mode); return first; }; } diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts b/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts deleted file mode 100644 index e485dc4..0000000 --- a/projects/emfular/src/lib/referencing/test/conversation-example.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { - Author, - ConversationPartner, - InformationLink, - InformationLinkType, - NewInformation, - ReceiveMessage, - SendMessage -} from "./conversation-example"; - -describe('Conversation Example', () => { - it('deletion of NewInformation should delete reference to source', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - }); - - it('deletion of NewInformation should delete reference to isUsedOn', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let sendMessA: SendMessage = SendMessage.create(partner, 0, 'Sent message A'); - let sendMessB: SendMessage = SendMessage.create(partner, 0, 'Sent message B'); - let author: Author = Author.create('Author'); - author.addMessage(sendMessA, sendMessB); - sendMessA.addUsage(newInfoA); - sendMessA.addUsage(newInfoB); - sendMessB.addUsage(newInfoA); - sendMessB.addUsage(newInfoB); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(newInfoA.isUsedOn.length).toBe(2); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(2); - expect(sendMessB.uses.length).toBe(2); - newInfoA.destruct(); - expect(author.messages.length).toBe(2); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(newInfoA.isUsedOn.length).toBe(0); - expect(newInfoB.isUsedOn.length).toBe(2); - expect(sendMessA.uses.length).toBe(1); - expect(sendMessB.uses.length).toBe(1); - }); - - it('deletion of NewInformation should delete reference to repeatedBy', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let recMessRepA: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepA'); - let recMessRepB: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message RepB'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - recMessRepA.addRepetition(newInfoA); - recMessRepA.addRepetition(newInfoB); - recMessRepB.addRepetition(newInfoA); - recMessRepB.addRepetition(newInfoB); - expect(newInfoA.source).toBeDefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(2); - expect(recMessRepA.repeats.length).toBe(2); - expect(recMessRepB.repeats.length).toBe(2); - expect(newInfoA.repeatedBy.length).toBe(2); - expect(newInfoB.repeatedBy.length).toBe(2); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoB.source).toBeDefined(); - expect(recMess.generates.length).toBe(1); - expect(recMessRepA.repeats.length).toBe(1); - expect(recMessRepB.repeats.length).toBe(1); - expect(newInfoA.repeatedBy.length).toBe(0); - expect(newInfoB.repeatedBy.length).toBe(2); - }); - - it('deletion of NewInformation should delete reference to causes and delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkAB: InformationLink = InformationLink.create(newInfoA, newInfoB, InformationLinkType.ATTACK); - let infoLinkAC: InformationLink = InformationLink.create(newInfoA, newInfoC, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.causes.length).toBe(2); - expect(infoLinkAB.source).toBeDefined(); - expect(infoLinkAC.source).toBeDefined(); - expect(infoLinkAB.target).toBeDefined(); - expect(infoLinkAC.target).toBeDefined(); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.causes.length).toBe(0); - expect(infoLinkAB.source).toBeUndefined(); - expect(infoLinkAC.source).toBeUndefined(); - expect(infoLinkAB.target).toBeUndefined(); - expect(infoLinkAC.target).toBeUndefined(); - }); - - it('deletion of NewInformation should delete reference to targetedBy and delete children', () => { - let partner: ConversationPartner = new ConversationPartner(); - let recMess: ReceiveMessage = ReceiveMessage.create(partner, 0, 'Received message'); - let newInfoA: NewInformation = NewInformation.create(recMess, 'Info A'); - let newInfoB: NewInformation = NewInformation.create(recMess, 'Info B'); - let newInfoC: NewInformation = NewInformation.create(recMess, 'Info C'); - let infoLinkBA: InformationLink = InformationLink.create(newInfoB, newInfoA, InformationLinkType.ATTACK); - let infoLinkCA: InformationLink = InformationLink.create(newInfoC, newInfoA, InformationLinkType.SUPPORT); - expect(newInfoA.source).toBeDefined(); - expect(newInfoA.targetedBy.length).toBe(2); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeDefined(); - expect(infoLinkCA.target).toBeDefined(); - newInfoA.destruct(); - expect(newInfoA.source).toBeUndefined(); - expect(newInfoA.targetedBy.length).toBe(0); - expect(infoLinkBA.source).toBeDefined(); - expect(infoLinkCA.source).toBeDefined(); - expect(infoLinkBA.target).toBeUndefined(); - expect(infoLinkCA.target).toBeUndefined(); - }); -}); \ No newline at end of file diff --git a/projects/emfular/src/lib/referencing/test/conversation-example.ts b/projects/emfular/src/lib/referencing/test/conversation-example.ts deleted file mode 100644 index bf2501a..0000000 --- a/projects/emfular/src/lib/referencing/test/conversation-example.ts +++ /dev/null @@ -1,430 +0,0 @@ -import { ReLinkListContainer } from "../referencable/container/link/re-link-list-container"; -import { ReLinkSingleContainer } from "../referencable/container/link/re-link-single-container"; -import { ReTreeListContainer } from "../referencable/container/tree/re-tree-list-container"; -import { ReTreeParentContainer } from "../referencable/container/shallow/re-tree-parent-container"; -import { ReTreeSingleContainer } from "../referencable/container/tree/re-tree-single-container"; -import { Referencable } from "../referencable/referenceable"; - -export enum InformationLinkType { - SUPPLEMENT = 'SUPPLEMENT', - SUPPORT = 'SUPPORT', - STRONG_SUPPORT = 'STRONG_SUPPORT', - ATTACK = 'ATTACK', - STRONG_ATTACK = 'STRONG_ATTACK', -} - -export class Conversation extends Referencable { - static readonly $authorName = 'author'; - static readonly $conversationPartnersName = 'conversationPartners'; - - title: string; - - _author: ReTreeSingleContainer; - get author(): Author { - return this._author.get()!! - } - set author(author: Author) { - this._author.add(author); - } - _conversationPartners: ReTreeListContainer; - get conversationPartners(): ConversationPartner[] { - return this._conversationPartners.get() - } - addCP(...cps: ConversationPartner[]) { - cps.map(cp => { - this._conversationPartners.add(cp) - }) - } - - constructor( - title: string = 'New Conversation' - ) { - super(); - this._author = new ReTreeSingleContainer(this, Conversation.$authorName); - this._conversationPartners = new ReTreeListContainer(this, Conversation.$conversationPartnersName); - this.title = title; - this.author = new Author(); - } - - static create(title: string = 'New conversation', author?: Author): Conversation { - const conv = new Conversation('New Conversation'); - conv.title = title; - conv.author = author? author: new Author(); - return conv; - } - -} - -export abstract class LifeLine extends Referencable{ - name: string; - xPosition: number; //int - - protected constructor(name?: string, xPosition: number = 0) { - super(); - this.name = name? name: ''; - this.xPosition = xPosition; - } - -} - -export class ConversationPartner extends LifeLine { - - constructor(name: string = 'NewPartner', xPosition?: number) { - super(name, xPosition); - } - - static create(name: string = 'NewPartner', xPosition?: number): ConversationPartner { - const cp = new ConversationPartner() - cp.name = name - cp.xPosition = xPosition? xPosition : 0; - return cp; - } - -} - -export class Author extends LifeLine{ - static readonly $preknowledgeName: string = 'preknowledge'; - static readonly $messagesName: string = 'messages'; - - _preknowledge: ReTreeListContainer; - get preknowledge(): Preknowledge[] { - return this._preknowledge.get() - } - addPreknowledge(...preknowledge: Preknowledge[]) { - preknowledge.map(p => { - this._preknowledge.add(p) - }) - } - - _messages: ReTreeListContainer; - get messages(): Message[] { - return this._messages.get() - } - addMessage(...msgs: Message[]) { - msgs.map(m => { - this._messages.add(m) - }) - } - - constructor() { - super(); - this._preknowledge = new ReTreeListContainer(this, Author.$preknowledgeName) - this._messages = new ReTreeListContainer(this, Author.$messagesName) - } - - static create(name?: string, xPosition: number = 0): Author { - const auth = new Author() - auth.name = name? name: '' - auth.xPosition = xPosition - return auth - } - -} - -export abstract class Message extends Referencable { - public static readonly $counterPartName = 'counterPart' - - _counterPart: ReLinkSingleContainer; - get counterPart(): ConversationPartner { - return this._counterPart.get()!! - } - set counterPart(value: ConversationPartner) { - this._counterPart.add(value); - } - - timing: number; - content: string; - originalContent?: string; - - protected constructor( - timing: number = 0, - content: string = "", - originalContent?: string, - ) { - super(); - this.timing = timing; - this.content = content; - this.originalContent = originalContent; - this._counterPart = new ReLinkSingleContainer(this, Message.$counterPartName) - } - - static isSend(eClass: string) { - return eClass.endsWith("SendMessage"); - } - - isSend(): this is SendMessage { - return this instanceof SendMessage - } - - isReceive(): this is ReceiveMessage { - return this instanceof ReceiveMessage - } - - static newMessage(isSend: boolean, counterPart: ConversationPartner, timing: number, content: string, originalContent: string = 'Original content'): Message { - if (isSend) { - return SendMessage.create(counterPart, timing, content, originalContent) - } else { - return ReceiveMessage.create(counterPart, timing, content, originalContent) - } - } -} - - -export class SendMessage extends Message { - public static readonly $usesName = 'uses' - - private readonly _uses: ReLinkListContainer; - get uses(): Information[] { - return this._uses.get(); - } - addUsage(info: Information) { - this._uses.add(info) - } - removeUsage(info: Information): boolean { - return this._uses.remove(info) - } - - constructor( - timing?: number, - content: string = 'New send content', - originalContent?: string, - ) { - super(timing, content, originalContent); - this._uses = new ReLinkListContainer(this, SendMessage.$usesName, Information.$isUsedOnName); - } - - static create(counterPart: ConversationPartner, - timing: number, - content: string = 'New send content', - originalContent?: string, - ): SendMessage { - const send = new SendMessage(timing, content, originalContent); - send.counterPart = counterPart; - return send; - } - -} - -export class ReceiveMessage extends Message { - static readonly $generatesName: string = 'generates'; - static readonly $repeatsName: string = 'repeats'; - - _generates: ReTreeListContainer; - get generates(): NewInformation[] { - return this._generates.get()!! - } - - _repeats: ReLinkListContainer; - get repeats(): Information[] { - return this._repeats.get(); - } - addRepetition(info: Information) { - this._repeats.add(info); - } - removeRepetition(info: Information): boolean { - return this._repeats.remove(info); - } - - isInterrupted: boolean = false; - - constructor( - timing?: number, - content: string = "New receive content", - originalContent?: string, - isInterrupted: boolean = false, - ) { - super(timing, content, originalContent); - this._generates = new ReTreeListContainer(this, ReceiveMessage.$generatesName, NewInformation.$sourceName); - this._repeats = new ReLinkListContainer(this, ReceiveMessage.$repeatsName, Information.$repeatedByName); - this.isInterrupted = isInterrupted; - } - - static create(counterPart: ConversationPartner, - timing: number, - content?: string, - originalContent?: string, - isInterrupted: boolean = false,): ReceiveMessage { - const rec = new ReceiveMessage(timing, content, originalContent, isInterrupted); - rec.counterPart = counterPart; - return rec - } - -} - -export abstract class Information< - P extends Referencable=Referencable -> extends Referencable

{ - - message: string = ""; - isInstruction: boolean = false; - initialTrust: number | undefined; - currentTrust: number | undefined; - feltTrustImmediately: number | undefined; - feltTrustAfterwards: number | undefined; - - abstract getTiming(): number; - - static readonly $causesName: string = 'causes' - static readonly $isUsedOnName: string = 'isUsedOn' - static readonly $repeatedByName: string = 'repeatedBy' - static readonly $targetedByName: string = 'targetedBy' - readonly _causes: ReTreeListContainer; - get causes(): InformationLink[] { - return this._causes.get(); - } - - readonly _targetedBy: ReLinkListContainer - get targetedBy(): InformationLink[] { - return this._targetedBy.get(); - } - - readonly _isUsedOn: ReLinkListContainer - get isUsedOn(): SendMessage[] { - return this._isUsedOn.get(); - } - addIsUsedOn(...send: SendMessage[]){ - send.map(s => this._isUsedOn.add(s)) - } - removeIsUsedOn(send: SendMessage){ - this._isUsedOn.remove(send) - } - - readonly _repeatedBy: ReLinkListContainer - get repeatedBy(): ReceiveMessage[] { - return this._repeatedBy.get(); - } - - addRepeatedBy(msg: ReceiveMessage) { - this._repeatedBy.add(msg) - } - removeRepeatedBy(msg: ReceiveMessage) { - this._repeatedBy.remove(msg) - } - - protected constructor() { - super(); - - this._causes = new ReTreeListContainer(this, NewInformation.$causesName, InformationLink.$sourceName); - this._targetedBy = new ReLinkListContainer(this, Information.$targetedByName, InformationLink.$targetName) - this._isUsedOn = new ReLinkListContainer(this, 'isUsedOn', 'uses'); - this._repeatedBy = new ReLinkListContainer(this, NewInformation.$repeatedByName, ReceiveMessage.$repeatsName); - } - - abstract duplicate(): Information; - -} -export class NewInformation extends Information { - - public static readonly $sourceName = 'source' - - readonly _source: ReTreeParentContainer; - set source(rec: ReceiveMessage) { - this._source.add(rec) - } - get source(): ReceiveMessage { - return this._source.get()!! - } - - override getTiming(): number { - return this.source.timing - } - - constructor() { - super(); - this._source = new ReTreeParentContainer(this, NewInformation.$sourceName, ReceiveMessage.$generatesName); - } - - override duplicate(): NewInformation { - return NewInformation.create(this.source, 'Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); - } - - static create(source: ReceiveMessage, - message: string, isInstruction: boolean = false, - initialTrust?: number, currentTrust?: number, feltTrustImmediately?: number, feltTrustAfterwards?: number,): NewInformation { - const info = new NewInformation(); - info.source = source; - info.message = message; - info.isInstruction = isInstruction; - info.initialTrust = initialTrust; - info.currentTrust = currentTrust; - info.feltTrustImmediately = feltTrustImmediately; - info.feltTrustAfterwards = feltTrustAfterwards; - return info; - } - -} - -export class Preknowledge extends Information { - - constructor() { - super(); - } - - getTiming(): number { - let timing; - if (this.isUsedOn?.length >0) { - timing = Math.min(...this.isUsedOn.map(send => send.timing)); - } else { - timing = 0 - } - return timing - } - - override duplicate(): Preknowledge { - return Preknowledge.create('Copy of ' + this.message, this.isInstruction, this.initialTrust, this.currentTrust, this.feltTrustImmediately, this.feltTrustAfterwards); - } - - static create(message: string = 'Preknowledge', isInstruction: boolean = false, - initialTrust?: number, currentTrust?: number, - feltTrustImmediately?: number, feltTrustAfterwards?: number): Preknowledge { - const pre = new Preknowledge() - pre.message = message - pre.isInstruction = isInstruction - pre.initialTrust = initialTrust - pre.currentTrust = currentTrust - pre.feltTrustImmediately = feltTrustImmediately - pre.feltTrustAfterwards = feltTrustAfterwards - return pre - } - -} - -export class InformationLink extends Referencable { - - public static readonly $sourceName = 'source' - public static readonly $targetName = 'target' - readonly _source: ReTreeParentContainer - get source(): Information { - return this._source.get()!!; - } - set source(source: Information) { - this._source.add(source) - } - - readonly _target: ReLinkSingleContainer - get target(): Information { - return this._target.get()!!; - } - set target(target: Information) { - this._target.add(target); - } - - type: InformationLinkType = InformationLinkType.SUPPLEMENT; - linkText?: string; - - constructor() { - super(); - this._source = new ReTreeParentContainer(this, InformationLink.$sourceName, NewInformation.$causesName); - this._target = new ReLinkSingleContainer(this, InformationLink.$targetName, Information.$targetedByName); - } - - static create(source: Information, target: Information, type: InformationLinkType, linkText?: string,): InformationLink { - const link = new InformationLink() - link.source = source - link.target = target; - link.type = type - link.linkText = linkText - return link - } - -} \ No newline at end of file From a75ba6dd9490fb574289a6f613e7237e91f65ab9 Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Tue, 31 Mar 2026 02:28:23 +0200 Subject: [PATCH 21/22] -added removeCascade to MetaAwareModelList interface -added missing behavior when calling ReTreeListContainer remove with deletion mode -fixed bug where calling destruct with CASCADE and the newly added behavior led to an infinite loop -added ReChild4 as an object with required link for testing -added more exhaustive tests for both deletion modes --- .../referencable/container/hide/model-list.ts | 1 + .../link/re-link-list-container.spec.ts | 84 +++++++++++++++++-- .../tree/re-tree-list-container.spec.ts | 81 ++++++++++++++++++ .../container/tree/re-tree-list-container.ts | 11 ++- .../referencing/referencable/referenceable.ts | 2 +- .../test/referencables-with-children.ts | 61 +++++++++++++- 6 files changed, 229 insertions(+), 11 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/hide/model-list.ts b/projects/emfular/src/lib/referencing/referencable/container/hide/model-list.ts index 6f4a1dc..2eda522 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/hide/model-list.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/hide/model-list.ts @@ -20,6 +20,7 @@ export interface MetaAwareModelList move(from: number, to: number): void; swap(from: number, to: number): void; remove(...items: T[]): boolean; + removeCascade(...items: T[]): boolean; delete(): void; readonly __item: T; diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts index 4cfb65b..6e633af 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-list-container.spec.ts @@ -1,6 +1,6 @@ import { ReLinkListContainer } from './re-link-list-container'; import {ReferencableTester, refTesterRef} from "../../../test/referencable-tester"; -import {RootWithChildren, ReChild3} from "../../../test/referencables-with-children"; +import {RootWithChildren, ReChild3, ReChild4, Middle2WithChildren} from "../../../test/referencables-with-children"; describe('ReLinkListContainer', () => { it('should create an instance', () => { @@ -8,14 +8,84 @@ describe('ReLinkListContainer', () => { expect(new ReLinkListContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); - it("should give true if the remove and remove inverse chain triggered an element removal", () => { + it("should give true if the remove and remove inverse chain triggered an element removal without removing element from different container", () => { let tester = new RootWithChildren() - let elem2 = new ReChild3() - expect(tester.link3.remove(elem2)).toBeFalse() - tester.link3.push(elem2) + let middle = new Middle2WithChildren() + let elem1 = new ReChild3() + let elem2 = new ReChild4() + expect(tester.link3.remove(elem1)).toBeFalse() + expect(tester.link4.remove(elem2)).toBeFalse() + tester.link3.push(elem1) + tester.link4.push(elem2) + middle.child3.push(elem1) + middle.child4.push(elem2) expect(tester.link3.length).toBe(1) - expect(tester.link3).toContain(elem2) - expect(tester.link3.remove(elem2)).toBeTrue() + expect(tester.link4.length).toBe(1) + expect(tester.link3).toContain(elem1) + expect(tester.link4).toContain(elem2) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(1) + expect(middle.child3).toContain(elem1) + expect(middle.child4).toContain(elem2) + expect(elem1.link1.length).toBe(1) + expect(elem2.link1.length).toBe(1) + expect(elem1.link1).toContain(tester) + expect(elem2.link1).toContain(tester) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeDefined() + expect(elem1.parentPointer).toEqual(middle) + expect(elem2.parentPointer).toEqual(middle) + expect(tester.link3.remove(elem1)).toBeTrue() + expect(tester.link4.remove(elem2)).toBeTrue() expect(tester.link3.length).toBe(0) + expect(tester.link4.length).toBe(0) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(1) + expect(elem1.link1.length).toBe(0) + expect(elem2.link1.length).toBe(0) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeDefined() + expect(elem1.parentPointer).toEqual(middle) + expect(elem2.parentPointer).toEqual(middle) + }); + + it("should give true if the removeCascade and remove inverse chain triggered an element removal, also removing it from other containers if required reference is deleted", () => { + let tester = new RootWithChildren() + let middle = new Middle2WithChildren() + let elem1 = new ReChild3() + let elem2 = new ReChild4() + expect(tester.link3.removeCascade(elem1)).toBeFalse() + expect(tester.link4.removeCascade(elem2)).toBeFalse() + tester.link3.push(elem1) + tester.link4.push(elem2) + middle.child3.push(elem1) + middle.child4.push(elem2) + expect(tester.link3.length).toBe(1) + expect(tester.link4.length).toBe(1) + expect(tester.link3).toContain(elem1) + expect(tester.link4).toContain(elem2) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(1) + expect(middle.child3).toContain(elem1) + expect(middle.child4).toContain(elem2) + expect(elem1.link1.length).toBe(1) + expect(elem2.link1.length).toBe(1) + expect(elem1.link1).toContain(tester) + expect(elem2.link1).toContain(tester) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeDefined() + expect(elem1.parentPointer).toEqual(middle) + expect(elem2.parentPointer).toEqual(middle) + expect(tester.link3.removeCascade(elem1)).toBeTrue() + expect(tester.link4.removeCascade(elem2)).toBeTrue() + expect(tester.link3.length).toBe(0) + expect(tester.link4.length).toBe(0) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(0) + expect(elem1.link1.length).toBe(0) + expect(elem2.link1.length).toBe(0) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeUndefined() + expect(elem1.parentPointer).toEqual(middle) }) }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts index 911d218..46c1d80 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.spec.ts @@ -1,9 +1,90 @@ import { ReTreeListContainer } from './re-tree-list-container'; import {ReferencableTester, refTesterRef} from "../../../test/referencable-tester"; +import {Middle2WithChildren, ReChild3, ReChild4, RootWithChildren} from "../../../test/referencables-with-children"; describe('ReferencableTreeListContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() expect(new ReTreeListContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); + + it("should give true if the remove and remove inverse chain triggered an element removal without removing element from different container", () => { + let tester = new RootWithChildren() + let middle = new Middle2WithChildren() + let elem1 = new ReChild3() + let elem2 = new ReChild4() + expect(tester.child2.remove(middle)).toBeFalse() + tester.link3.push(elem1) + tester.link4.push(elem2) + tester.child2.push(middle) + middle.child3.push(elem1) + middle.child4.push(elem2) + expect(tester.link3.length).toBe(1) + expect(tester.link4.length).toBe(1) + expect(tester.link3).toContain(elem1) + expect(tester.link4).toContain(elem2) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(1) + expect(middle.child3).toContain(elem1) + expect(middle.child4).toContain(elem2) + expect(elem1.link1.length).toBe(1) + expect(elem2.link1.length).toBe(1) + expect(elem1.link1).toContain(tester) + expect(elem2.link1).toContain(tester) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeDefined() + expect(elem1.parentPointer).toEqual(middle) + expect(elem2.parentPointer).toEqual(middle) + expect(tester.child2.remove(middle)).toBeTrue() + expect(tester.child2.length).toBe(0) + expect(tester.link3.length).toBe(1) + expect(tester.link4.length).toBe(1) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(1) + expect(elem1.link1.length).toBe(1) + expect(elem2.link1.length).toBe(1) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeDefined() + expect(elem1.parentPointer).toEqual(middle) + expect(elem2.parentPointer).toEqual(middle) + }); + + it("should give true if the removeCascade and remove inverse chain triggered an element removal, also removing it from other containers if required reference is deleted", () => { + let tester = new RootWithChildren() + let middle = new Middle2WithChildren() + let elem1 = new ReChild3() + let elem2 = new ReChild4() + expect(tester.child2.removeCascade(middle)).toBeFalse() + tester.link3.push(elem1) + tester.link4.push(elem2) + tester.child2.push(middle) + middle.child3.push(elem1) + middle.child4.push(elem2) + expect(tester.link3.length).toBe(1) + expect(tester.link4.length).toBe(1) + expect(tester.link3).toContain(elem1) + expect(tester.link4).toContain(elem2) + expect(middle.child3.length).toBe(1) + expect(middle.child4.length).toBe(1) + expect(middle.child3).toContain(elem1) + expect(middle.child4).toContain(elem2) + expect(elem1.link1.length).toBe(1) + expect(elem2.link1.length).toBe(1) + expect(elem1.link1).toContain(tester) + expect(elem2.link1).toContain(tester) + expect(elem1.parentPointer).toBeDefined() + expect(elem2.parentPointer).toBeDefined() + expect(elem1.parentPointer).toEqual(middle) + expect(elem2.parentPointer).toEqual(middle) + expect(tester.child2.removeCascade(middle)).toBeTrue() + expect(tester.child2.length).toBe(0) + expect(tester.link3.length).toBe(0) + expect(tester.link4.length).toBe(0) + expect(middle.child3.length).toBe(0) + expect(middle.child4.length).toBe(0) + expect(elem1.link1.length).toBe(0) + expect(elem2.link1.length).toBe(0) + expect(elem1.parentPointer).toBeUndefined() + expect(elem2.parentPointer).toBeUndefined() + }) }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts index 9a1abf7..5d9e1bf 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts @@ -5,7 +5,7 @@ import {JsonOf} from "../../../../serialization/json-deserializable"; import {SerializationContext} from "../../../../serialization/serialization-context"; import {ReTreeChildrenContainer} from "./re-tree-children-container"; import {ListUpdater} from "../../../../utils/list-updater"; -import { DeletionMode } from "../../../../utils/deletion-mode"; +import {DeletionMode} from "../../../../utils/deletion-mode"; import {ReListContainer} from "../re-list-container"; import {ReferenceMeta} from "../../../../binding/model-definition"; @@ -46,7 +46,14 @@ implements ReTreeChildrenContainer { } } - override remove(item: T): boolean { + override remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { + if (mode == DeletionMode.CASCADE) { + if (this._instance.indexOf(item) > -1) { + item.destruct(DeletionMode.CASCADE); + return true; + } + return false; + } let removed = ListUpdater.removeFromList(item, this._instance) if(removed){ item.setParent(undefined); diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index 9dd82bb..7ccd94c 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -76,7 +76,7 @@ export abstract class Referencable< } destruct(mode: DeletionMode = DeletionMode.RELAXED) { - this.$parent?.remove(this, mode) + this.$parent?.remove(this) this.$otherReferences.forEach(refContainer => { refContainer.removeFromInverse(this, mode) }) diff --git a/projects/emfular/src/lib/referencing/test/referencables-with-children.ts b/projects/emfular/src/lib/referencing/test/referencables-with-children.ts index cf04ce9..f87be80 100644 --- a/projects/emfular/src/lib/referencing/test/referencables-with-children.ts +++ b/projects/emfular/src/lib/referencing/test/referencables-with-children.ts @@ -25,6 +25,12 @@ export const ModelWithChildren: ModelDefinition = { opposite: "link1", min: 0, max: -1 + }, + link4: { + target: "ReChild4", + opposite: "link1", + min: 0, + max: -1 } } }, @@ -37,6 +43,13 @@ export const ModelWithChildren: ModelDefinition = { opposite: "parentPointer", min: 0, max: -1 + }, + child4: { + target: "ReChild4", + containment: true, + opposite: "parentPointer", + min: 0, + max: -1 } } }, @@ -58,6 +71,24 @@ export const ModelWithChildren: ModelDefinition = { max: 1 } } + }, + ReChild4: { + references: { + link1: { + target: "RootWithChildren", + opposite: "link4", + min: 1, + max: -1 + }, + parentPointer: { + target: "Middle2WithChildren", + containment: true, + isParent: true, + opposite: "child4", + min: 0, + max: 1 + } + } } } } as const; @@ -65,19 +96,26 @@ export const ModelWithChildren: ModelDefinition = { export const RootWithChildrenRefs = { child2: ModelWithChildren.classes["RootWithChildren"].references["child2"], link3: ModelWithChildren.classes["RootWithChildren"].references["link3"], + link4: ModelWithChildren.classes["RootWithChildren"].references["link4"] } as const; export const Middle2WithChildrenRefs = { child3: ModelWithChildren.classes["Middle2WithChildren"].references["child3"], + child4: ModelWithChildren.classes["Middle2WithChildren"].references["child4"] } as const; export const ReChild3Refs = { link1: ModelWithChildren.classes["ReChild3"].references["link1"], parentPointer: ModelWithChildren.classes["ReChild3"].references["parentPointer"], } as const; +export const ReChild4Refs = { + link1: ModelWithChildren.classes["ReChild4"].references["link1"], + parentPointer: ModelWithChildren.classes["ReChild4"].references["parentPointer"], +} as const; export enum EClasses { 'RootWithChildren' = 'namespace/RootWithChildren', 'Middle2WithChildren' = 'namespace/Middle2WithChildren', - 'ReChild3' = 'namespace/ReChild3' + 'ReChild3' = 'namespace/ReChild3', + 'ReChild4' = 'namespace/ReChild4' } @eClass(ModelWithChildren, "RootWithChildren") @@ -87,6 +125,8 @@ export class RootWithChildren extends Referencable { declare child2: ModelList @reference(RootWithChildrenRefs.link3) declare link3: ModelList; + @reference(RootWithChildrenRefs.link4) + declare link4: ModelList; @attribute() name: string = "referencable1"; @@ -101,6 +141,8 @@ export class Middle2WithChildren extends Referencable { @reference(Middle2WithChildrenRefs.child3) declare child3: ModelList + @reference(Middle2WithChildrenRefs.child4) + declare child4: ModelList @attribute() name: string = "referencable2"; @@ -126,8 +168,25 @@ export class ReChild3 extends Referencable { } } +@eClass(ModelWithChildren, "ReChild4") +export class ReChild4 extends Referencable { + @reference(ReChild4Refs.link1) + declare link1: ModelList + + @reference(ReChild4Refs.parentPointer) + declare parentPointer?: Middle2WithChildren; + + @attribute() + name: string = "referencable4"; + + constructor() { + super(); + } +} + export type RootWithChildrenJson = JsonOf export type Middle2WithChildrenJson = JsonOf export type ReChild3Json = JsonOf +export type ReChild4Json = JsonOf From f6f25854495ee2956275f8eced7ce5ba60e0e00b Mon Sep 17 00:00:00 2001 From: "@nheuser" Date: Thu, 9 Apr 2026 02:51:06 +0200 Subject: [PATCH 22/22] -added tests for removing elements in single containers -added comment to highlight correctness of leaving out mode argument when removing itself from parent -replaced == with type-safe === for extra caution -fixed bug causing infinite loop when calling remove of ReLinkSingleContainer -added DeletionMode to remove of ReTreeSingleContainer --- .../link/re-link-single-container.spec.ts | 93 ++++++++++++++++++- .../link/re-link-single-container.ts | 2 +- .../container/tree/re-tree-list-container.ts | 4 +- .../tree/re-tree-single-container.spec.ts | 46 +++++++++ .../tree/re-tree-single-container.ts | 12 ++- .../referencing/referencable/referenceable.ts | 9 +- .../test/re-containers-with-single-child.ts | 88 +++++++++++++++++- 7 files changed, 241 insertions(+), 13 deletions(-) diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts index 66872b5..5e282f5 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.spec.ts @@ -1,9 +1,100 @@ -import { ReLinkSingleContainer } from './re-link-single-container'; +import {ReLinkSingleContainer} from './re-link-single-container'; import {ReferencableTester, refTesterRef} from "../../../test/referencable-tester"; +import { + ReContainersWithSingleChild, + ReContainersWithSingleChild2, + ReSingleChildExample, + ReSingleChildExample2 +} from "../../../test/re-containers-with-single-child"; +import {DeletionMode} from "../../../../utils/deletion-mode"; describe('ReLinkSingleContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() expect(new ReLinkSingleContainer(tester, 'refName', refTesterRef.references.test)).toBeTruthy(); }); + + it('should remove child from container without it getting removed from other containers, even if required reference is removed', () => { + let tester1 = new ReContainersWithSingleChild(); + let middle1 = new ReSingleChildExample(); + let elem1 = new ReContainersWithSingleChild(); + let tester2 = new ReContainersWithSingleChild2(); + let middle2 = new ReSingleChildExample2(); + let elem2 = new ReContainersWithSingleChild2(); + tester1.child = middle1; + middle1.otherLink = elem1; + tester2.child = middle2; + middle2.otherLink = elem2; + expect(tester1.child).toBeDefined(); + expect(tester1.child).toEqual(middle1); + expect(tester2.child).toBeDefined(); + expect(tester2.child).toEqual(middle2); + expect(middle1.myParent).toBeDefined(); + expect(middle1.myParent).toEqual(tester1); + expect(middle2.myParent).toBeDefined(); + expect(middle2.myParent).toEqual(tester2); + expect(middle1.otherLink).toBeDefined(); + expect(middle1.otherLink).toEqual(elem1); + expect(middle2.otherLink).toBeDefined(); + expect(middle2.otherLink).toEqual(elem2); + expect(elem1.link).toBeDefined(); + expect(elem1.link).toEqual(middle1); + expect(elem2.link).toBeDefined(); + expect(elem2.link).toEqual(middle2); + expect(elem1.$otherReferences[0].remove(middle1)).toBeTrue(); + expect(elem2.$otherReferences[0].remove(middle2)).toBeTrue(); + expect(tester1.child).toBeDefined(); + expect(tester1.child).toEqual(middle1); + expect(tester2.child).toBeDefined(); + expect(tester2.child).toEqual(middle2); + expect(middle1.myParent).toBeDefined(); + expect(middle1.myParent).toEqual(tester1); + expect(middle2.myParent).toBeDefined(); + expect(middle2.myParent).toEqual(tester2); + expect(middle1.otherLink).toBeUndefined(); + expect(middle2.otherLink).toBeUndefined(); + expect(elem1.link).toBeUndefined(); + expect(elem2.link).toBeUndefined(); + }); + + it('should remove reference from container, triggering its deletion in case of required reference getting removed', () => { + let tester1 = new ReContainersWithSingleChild(); + let middle1 = new ReSingleChildExample(); + let elem1 = new ReContainersWithSingleChild(); + let tester2 = new ReContainersWithSingleChild2(); + let middle2 = new ReSingleChildExample2(); + let elem2 = new ReContainersWithSingleChild2(); + tester1.child = middle1; + middle1.otherLink = elem1; + tester2.child = middle2; + middle2.otherLink = elem2; + expect(tester1.child).toBeDefined(); + expect(tester1.child).toEqual(middle1); + expect(tester2.child).toBeDefined(); + expect(tester2.child).toEqual(middle2); + expect(middle1.myParent).toBeDefined(); + expect(middle1.myParent).toEqual(tester1); + expect(middle2.myParent).toBeDefined(); + expect(middle2.myParent).toEqual(tester2); + expect(middle1.otherLink).toBeDefined(); + expect(middle1.otherLink).toEqual(elem1); + expect(middle2.otherLink).toBeDefined(); + expect(middle2.otherLink).toEqual(elem2); + expect(elem1.link).toBeDefined(); + expect(elem1.link).toEqual(middle1); + expect(elem2.link).toBeDefined(); + expect(elem2.link).toEqual(middle2); + expect(elem1.$otherReferences[0].remove(middle1, DeletionMode.CASCADE)).toBeTrue(); + expect(elem2.$otherReferences[0].remove(middle2, DeletionMode.CASCADE)).toBeTrue(); + expect(tester1.child).toBeDefined(); + expect(tester1.child).toEqual(middle1); + expect(tester2.child).toBeUndefined(); + expect(middle1.myParent).toBeDefined(); + expect(middle1.myParent).toEqual(tester1); + expect(middle2.myParent).toBeUndefined(); + expect(middle1.otherLink).toBeUndefined(); + expect(middle2.otherLink).toBeUndefined(); + expect(elem1.link).toBeUndefined(); + expect(elem2.link).toBeUndefined(); + }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts index 786220f..7cb2fec 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/link/re-link-single-container.ts @@ -38,10 +38,10 @@ implements ReLinkContainer { remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { if(this._instance == item) { + this._instance = undefined; if (this.inverseName != undefined) { item.removeFromReferencableContainer(this.inverseName, this._parent, mode) } - this._instance = undefined; return true; } else { return false; diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts index 5d9e1bf..076546f 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-list-container.ts @@ -47,9 +47,9 @@ implements ReTreeChildrenContainer { } override remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { - if (mode == DeletionMode.CASCADE) { + if (mode === DeletionMode.CASCADE) { if (this._instance.indexOf(item) > -1) { - item.destruct(DeletionMode.CASCADE); + item.destruct(mode); return true; } return false; diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts index 5fd42bf..1fe0916 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.spec.ts @@ -1,9 +1,55 @@ import { ReTreeSingleContainer } from './re-tree-single-container'; import {ReferencableTester, refTesterRef} from "../../../test/referencable-tester"; +import {ReContainersWithSingleChild, ReSingleChildExample} from "../../../test/re-containers-with-single-child"; +import {DeletionMode} from "../../../../utils/deletion-mode"; describe('ReferencableTreeSingletonContainer', () => { it('should create an instance', () => { let tester = new ReferencableTester() expect(new ReTreeSingleContainer(tester, 'test', refTesterRef.references.test)).toBeTruthy(); }); + + it('should be working', () => { + let tester = new ReContainersWithSingleChild(); + let middle = new ReSingleChildExample(); + let elem1 = new ReContainersWithSingleChild(); + tester.child = middle; + middle.otherLink = elem1; + expect(tester.child).toBeDefined(); + expect(tester.child).toEqual(middle); + expect(middle.myParent).toBeDefined(); + expect(middle.myParent).toEqual(tester); + expect(middle.otherLink).toBeDefined(); + expect(middle.otherLink).toEqual(elem1); + expect(elem1.link).toBeDefined(); + expect(elem1.link).toEqual(middle); + expect(tester.$treeChildren[0].remove(middle)).toBeTrue(); + expect(tester.child).toBeUndefined(); + expect(middle.myParent).toBeUndefined(); + expect(middle.otherLink).toBeDefined(); + expect(middle.otherLink).toEqual(elem1); + expect(elem1.link).toBeDefined(); + expect(elem1.link).toEqual(middle); + }); + + it('should be working 2', () => { + let tester = new ReContainersWithSingleChild(); + let middle = new ReSingleChildExample(); + let elem1 = new ReContainersWithSingleChild(); + tester.child = middle; + middle.otherLink = elem1; + expect(tester.child).toBeDefined(); + expect(tester.child).toEqual(middle); + expect(middle.myParent).toBeDefined(); + expect(middle.myParent).toEqual(tester); + expect(middle.otherLink).toBeDefined(); + expect(middle.otherLink).toEqual(elem1); + expect(elem1.link).toBeDefined(); + expect(elem1.link).toEqual(middle); + expect(tester.$treeChildren[0].remove(middle, DeletionMode.CASCADE)).toBeTrue(); + expect(tester.child).toBeUndefined(); + expect(middle.myParent).toBeUndefined(); + expect(middle.otherLink).toBeUndefined(); + expect(elem1.link).toBeUndefined(); + }); }); diff --git a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts index 58404c9..6487431 100644 --- a/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts +++ b/projects/emfular/src/lib/referencing/referencable/container/tree/re-tree-single-container.ts @@ -6,7 +6,7 @@ import {SerializationContext} from "../../../../serialization/serialization-cont import {ReTreeChildrenContainer} from "./re-tree-children-container"; import {ReSingleContainer} from "../re-single-container"; import {ReferenceMeta} from "../../../../binding/model-definition"; -import { DeletionMode } from "../../../../utils/deletion-mode"; +import {DeletionMode} from "../../../../utils/deletion-mode"; export class ReTreeSingleContainer> extends ReSingleContainer @@ -38,10 +38,14 @@ implements ReTreeChildrenContainer { } } - remove(item: T): boolean { + remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { if(this._instance == item) { - this._instance = undefined; - item.setParent(undefined); + if (mode === DeletionMode.RELAXED) { + this._instance = undefined; + item.setParent(undefined); + return true; + } + this._instance?.destruct(mode); return true; } return false; diff --git a/projects/emfular/src/lib/referencing/referencable/referenceable.ts b/projects/emfular/src/lib/referencing/referencable/referenceable.ts index 7ccd94c..a87d5c6 100644 --- a/projects/emfular/src/lib/referencing/referencable/referenceable.ts +++ b/projects/emfular/src/lib/referencing/referencable/referenceable.ts @@ -1,5 +1,5 @@ import {Ref} from "../ref/ref"; -import { v4 as uuidv4 } from 'uuid'; +import {v4 as uuidv4} from 'uuid'; import {ReContainer} from "./container/re-container"; import {Deserializer} from "../../serialization/deserializer"; import {getAllAttributes} from "../../binding/attribute-collector"; @@ -11,7 +11,7 @@ import {ReTreeChildrenContainer} from "./container/tree/re-tree-children-contain import {ReLinkContainer} from "./container/link/re-link-container"; import {ModelRegistry} from "../../binding/model-registry"; import {ClassMeta, ModelDefinition, ReferenceMeta} from "../../binding/model-definition"; -import { DeletionMode } from "../../utils/deletion-mode"; +import {DeletionMode} from "../../utils/deletion-mode"; /** base class for CORE models. * @@ -76,6 +76,7 @@ export abstract class Referencable< } destruct(mode: DeletionMode = DeletionMode.RELAXED) { + // removal from parent is always called with deletion mode RELAXED, otherwise infinite loops occur this.$parent?.remove(this) this.$otherReferences.forEach(refContainer => { refContainer.removeFromInverse(this, mode) @@ -119,9 +120,9 @@ export abstract class Referencable< public removeFromReferencableContainer>(name: string, item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean { let container = this.getContainer(name) let result = container.remove(item, mode) - if (result && mode == DeletionMode.CASCADE && container.isRequired) { + if (result && mode === DeletionMode.CASCADE && container.isRequired) { const instance = container.get() - if (instance == undefined || (Array.isArray(instance) && instance.length == 0)) { + if (instance === undefined || (Array.isArray(instance) && instance.length === 0)) { container._parent.destruct(mode) } } diff --git a/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts b/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts index 40582c1..c86fa65 100644 --- a/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts +++ b/projects/emfular/src/lib/referencing/test/re-containers-with-single-child.ts @@ -27,6 +27,21 @@ export const ModelSingleChild: ModelDefinition = { } } }, + ReContainersWithSingleChild2: { + references: { + child: { + target: "ReSingleChildExample2", + containment: true, + opposite: "myParent", + max: 1 + }, + link: { + target: "ReSingleChildExample2", + opposite: "otherLink", + max: 1 + } + } + }, ReSingleChildExample: { references: { @@ -42,6 +57,23 @@ export const ModelSingleChild: ModelDefinition = { max: 1 } } + }, + + ReSingleChildExample2: { + references: { + myParent: { + target: "ReContainersWithSingleChild2", + isParent: true, + opposite: "child", + max: 1 + }, + otherLink: { + target: "ReContainersWithSingleChild2", + opposite: "link", + min: 1, + max: 1 + } + } } } } as const; @@ -52,14 +84,26 @@ export const ReContainersWithSingleChildRefs = { link: ModelSingleChild.classes["ReContainersWithSingleChild"].references["link"] }; +export const ReContainersWithSingleChild2Refs = { + child: ModelSingleChild.classes["ReContainersWithSingleChild2"].references["child"], + link: ModelSingleChild.classes["ReContainersWithSingleChild2"].references["link"] +}; + export const ReSingleChildExampleRefs = { myParent: ModelSingleChild.classes["ReSingleChildExample"].references["myParent"], otherLink: ModelSingleChild.classes["ReSingleChildExample"].references["otherLink"] }; +export const ReSingleChildExample2Refs = { + myParent: ModelSingleChild.classes["ReSingleChildExample2"].references["myParent"], + otherLink: ModelSingleChild.classes["ReSingleChildExample2"].references["otherLink"] +}; + export enum EClassesSingleChild { 'ReContainersWithSingleChild' = 'class://ReContainersWithSingleChild', - 'ReSingleChildExample' = 'class://ReSingleChildExample' + 'ReContainersWithSingleChild2' = 'class://ReContainersWithSingleChild2', + 'ReSingleChildExample' = 'class://ReSingleChildExample', + 'ReSingleChildExample2' = 'class://ReSingleChildExample2' } @eClass(ModelSingleChild, "ReContainersWithSingleChild") @@ -87,6 +131,31 @@ export class ReContainersWithSingleChild extends Referencable { } +@eClass(ModelSingleChild, "ReContainersWithSingleChild2") +export class ReContainersWithSingleChild2 extends Referencable { + + @reference(ReContainersWithSingleChild2Refs.child) + declare child: ReSingleChildExample2 | undefined; + + @reference(ReContainersWithSingleChild2Refs.link) + declare link: ReSingleChildExample2 | undefined; + + @attribute() + name: string = "re1"; + + constructor() { + super(); + } + + static fromJSON (convJson: JsonOf): ReContainersWithSingleChild2 { + return Deserializer.fromJSON( + convJson, + EClassesSingleChild.ReContainersWithSingleChild2 + ) + } + +} + @eClass(ModelSingleChild, "ReSingleChildExample") export class ReSingleChildExample extends Referencable { @@ -103,3 +172,20 @@ export class ReSingleChildExample extends Referencable { + + @reference(ReSingleChildExample2Refs.myParent) + declare myParent: ReContainersWithSingleChild2 | undefined; + + @reference(ReSingleChildExample2Refs.otherLink) + declare otherLink: ReContainersWithSingleChild2 | undefined; + + @attribute() + myBool = true; + + constructor() { + super(); + } +}