Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
3e4a0e3
added enum with deletion modes
Noah1891 Feb 23, 2026
3a01d50
added isRequired flag to ReContainer
Noah1891 Feb 23, 2026
ce38944
remove isRequired flag from ReContainer
Noah1891 Feb 24, 2026
89672e3
added mode arguments to destruct, destructAllFromChangingList and delete
Noah1891 Feb 24, 2026
1fd5913
added mode arguments to remove() calls
Noah1891 Feb 24, 2026
6b369cb
added isRequired to ReContainer and the classes that inherit it
Noah1891 Feb 25, 2026
d454ea4
check for isRequired flag removeFromReferencableContainer() method an…
Noah1891 Feb 25, 2026
3d44b80
updated test files to be compatible with new isRequired flag
Noah1891 Mar 3, 2026
370b45d
adapted removeFromReferenceableContainer, destructAllFromChangingList…
Noah1891 Mar 3, 2026
a75705d
added minimal keml for testing deletion modes
Noah1891 Mar 3, 2026
4509e84
Updated tests and potential bug fix in removeFromInverse()
Noah1891 Mar 10, 2026
a16c889
Finished tests for new deletion modes
Noah1891 Mar 10, 2026
0342d96
updated test name to fix inconsistency
Noah1891 Mar 10, 2026
9d9a121
renamed test files
Noah1891 Mar 17, 2026
7d8c3e7
Merge branch 'main' into 14-offer-different-strategies-for-model-cons…
Noah1891 Mar 18, 2026
5bc1be0
fixed small mistakes after resolving merge conflicts
Noah1891 Mar 18, 2026
57f0529
removed isRequired from constructors and set it depending on meta.min…
Noah1891 Mar 23, 2026
fabc8be
Merge branch 'main' into 14-offer-different-strategies-for-model-cons…
Noah1891 Mar 23, 2026
3cfd611
deleted old incompatible tests
Noah1891 Mar 23, 2026
1abcb3f
added deleted tests again in incompatible state
Noah1891 Mar 23, 2026
1a9dcc9
added deleted classes for tests again in incompatible state
Noah1891 Mar 23, 2026
972a8e8
deleted conversation test classes for future replacement and updated …
Noah1891 Mar 29, 2026
a75ba6d
-added removeCascade to MetaAwareModelList interface
Noah1891 Mar 31, 2026
f6f2585
-added tests for removing elements in single containers
Noah1891 Apr 9, 2026
ad4d6d4
Updated tests to be consistent with changes in referencables-with-chi…
Noah1891 Apr 13, 2026
528e2e5
added meaningful test names to re-tree-single-container.spec.ts
Noah1891 Apr 13, 2026
da0ef64
deleted unused example classes from re-container-with-single-child.ts
Noah1891 Apr 14, 2026
5fa30b0
undid last commit, since removed classes where mistakenly labeled as …
Noah1891 Apr 23, 2026
959b42d
Explicitly set mode for parent removal
Noah1891 Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import {Referencable} from "../../referenceable";
import { ReContainer } from "../re-container";
import {SerializationContext} from "../../../../serialization/serialization-context";
import {Ref} from "../../../ref/ref";
import { DeletionMode } from "../../../../utils/deletion-mode";

export interface ReLinkContainer<
T extends Referencable<any>,
P extends Referencable<any>
> extends ReContainer<T,P> {

/*protected constructor(parent: P, referenceName: string, inverseName?: string) {
super(parent, referenceName, inverseName);
/*protected constructor(parent: P, referenceName: string, isRequired: boolean, inverseName?: string) {
super(parent, referenceName, isRequired, inverseName);
this._parent.$otherReferences.push(this)
}*/

removeFromInverse(item: T): boolean;
removeFromInverse(item: T, mode?: DeletionMode): boolean;

toJson(ctx: SerializationContext): Ref[] | Ref | undefined
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +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";

export class ReLinkListContainer<
T extends Referencable<any>,
Expand Down Expand Up @@ -33,24 +34,24 @@ implements ReLinkContainer<T, P> {
return this._instance.map(i => ctx.get(i))
}

override remove(item: T): boolean {
override remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): 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??
}

override delete() {
ListUpdater.destructAllFromChangingList(this._instance)
override delete(mode: DeletionMode = DeletionMode.RELAXED) {
ListUpdater.destructAllFromChangingList(this._instance, mode)
}

removeFromInverse(item: T): boolean {
removeFromInverse(item: T, mode: DeletionMode = DeletionMode.RELAXED): 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?
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {SerializationContext} from "../../../../serialization/serialization-cont
import {ReLinkContainer} from "./re-link-container";
import {ReSingleContainer} from "../re-single-container";
import {ReferenceMeta} from "../../../../binding/model-definition";
import { DeletionMode } from "../../../../utils/deletion-mode";

export class ReLinkSingleContainer<
T extends Referencable<any>,
Expand All @@ -18,7 +19,7 @@ implements ReLinkContainer<T, P> {

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 {
Expand All @@ -35,10 +36,10 @@ implements ReLinkContainer<T, P> {
}
}

remove(item: T): boolean {
remove(item: T, mode: DeletionMode = DeletionMode.RELAXED): 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;
Expand All @@ -47,13 +48,13 @@ implements ReLinkContainer<T, P> {
}
}

override delete() {
this._instance?.destruct()
override delete(mode: DeletionMode = DeletionMode.RELAXED) {
this._instance?.destruct(mode)
}

removeFromInverse(item: T): boolean {
removeFromInverse(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean {
if(this.inverseName !== undefined) {
this._instance?.removeFromReferencableContainer(this.inverseName, item)
this._instance?.removeFromReferencableContainer(this.inverseName, item, mode)
return true; // todo refine?
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +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";

export abstract class ReContainer<
T extends Referencable<any>,
Expand All @@ -11,11 +12,13 @@ export abstract class ReContainer<
readonly meta: ReferenceMeta;
readonly referenceName: string;
readonly inverseName?: string;
readonly isRequired: boolean;

protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) {
this._parent = parent;
this.meta = refMeta;
this.referenceName = referenceName;
this.isRequired = this.meta.min != undefined && this.meta.min > 0;
this.inverseName = refMeta.opposite;
}

Expand Down Expand Up @@ -43,10 +46,10 @@ export abstract class ReContainer<
return this.isAcceptableItem( new srcConstr()) //todo
}

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(): void
abstract delete(mode?: DeletionMode): void

abstract toJson(ctx: SerializationContext): any
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down Expand Up @@ -32,8 +33,8 @@ implements ReListInterface<T, P>{
return this._proxy;
}

override delete() {
ListUpdater.destructAllFromChangingList(this._instance)
override delete(mode: DeletionMode = DeletionMode.RELAXED) {
ListUpdater.destructAllFromChangingList(this._instance, mode)
}

move(from: number, to: number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ implements ReSingleInterface<T, P>{

protected _instance?: T ;

protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta ) {
protected constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) {
super(parent, referenceName, refMeta);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class ReDerivedListContainer<
parent: P,
computeOrSymbol: ((owner: P) => T[]) | symbol,
referenceName: string,
refMeta: ReferenceMeta,
refMeta: ReferenceMeta
) {
super(parent, referenceName, refMeta);
this.resolver = new ReDerivationResolver(computeOrSymbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {ReContainer} from "../re-container";
import {ReSingleInterface} from "../re-single-interface";
import {ReShallowInterface} from "./re-shallow-interface";
import {ReferenceMeta} from "../../../../binding/model-definition";
import { DeletionMode } from "../../../../utils/deletion-mode";

export class ReTreeParentContainer<T extends Referencable<any>>
extends ReContainer<T["ParentType"],T>
Expand All @@ -23,13 +24,13 @@ implements ReSingleInterface<T["ParentType"], T>,
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 = DeletionMode.RELAXED): boolean {
return item.removeFromReferencableContainer(this.inverseName, this._parent, mode)
}

delete(): void {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
import {ReListContainer} from "../re-list-container";
import {ReferenceMeta} from "../../../../binding/model-definition";

Expand Down Expand Up @@ -54,8 +55,8 @@ implements ReTreeChildrenContainer<T> {
return false;
}

override delete() {
ListUpdater.destructAllFromChangingList(this._instance)
override delete(mode: DeletionMode = DeletionMode.RELAXED) {
ListUpdater.destructAllFromChangingList(this._instance, mode)
}

//creates one child level plus calls next createChildren
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +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";

export class ReTreeSingleContainer<T extends Referencable<any>>
extends ReSingleContainer<T, T["ParentType"]>
Expand Down Expand Up @@ -46,8 +47,12 @@ implements ReTreeChildrenContainer<T> {
return false;
}

delete() {
this._instance?.destruct()
delete(mode: DeletionMode = DeletionMode.RELAXED) {
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) {
Expand Down
22 changes: 15 additions & 7 deletions projects/emfular/src/lib/referencing/referencable/referenceable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +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";

/** base class for CORE models.
*
Expand Down Expand Up @@ -74,13 +75,13 @@ export abstract class Referencable<
}
}

destruct() {
this.$parent?.remove(this)
destruct(mode: DeletionMode = DeletionMode.RELAXED) {
this.$parent?.remove(this, mode)
this.$otherReferences.forEach(refContainer => {
refContainer.removeFromInverse(this)
refContainer.removeFromInverse(this, mode)
})
this.$treeChildren.forEach(child => {
child.delete()
child.delete(mode)
})
}

Expand Down Expand Up @@ -111,13 +112,20 @@ export abstract class Referencable<
return container as ReContainer<T, Parent>;
}


public addToReferencableContainer<T extends Referencable<any>>(name: string, item: T): boolean {
return this.getContainer<T>(name).add(item)
}

public removeFromReferencableContainer<T extends Referencable<any>>(name: string, item: T): boolean {
return this.getContainer<T>(name).remove(item)
public removeFromReferencableContainer<T extends Referencable<any>>(name: string, item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean {
let container = this.getContainer<T>(name)
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 result
}

toJson(ctxOPt?: SerializationContext): JsonOf<this> {
Expand Down
Loading