Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c13a33e
Renmae ParentType to
SusieGoebel May 27, 2026
b0e7a00
Replace internal $parent with _$, and also accessor parent with $
SusieGoebel May 27, 2026
e071277
change setParent to real setter
SusieGoebel May 27, 2026
c00ba18
Prefix more methods with $, only toJson() should stay without prefix …
SusieGoebel May 27, 2026
d773ee8
Make another private method private, also define not exported symbol …
SusieGoebel May 27, 2026
f482244
All private methods are only via private symbols now
SusieGoebel May 27, 2026
6188cbf
replace assign refs by symbol SERIALIZE_ASSIGN_REFS
SusieGoebel May 28, 2026
ac99b75
Reorganize Referencable methods by purpose, to make more symbolized
SusieGoebel May 28, 2026
30d0bb7
New symbols add to Reference and remove from reference
SusieGoebel May 28, 2026
52c362c
Remove explicit parent removal, maybe cause of circular on strict mode
SusieGoebel May 28, 2026
8f09bbd
Rename all referencable symbols to have the class Referencable explic…
SusieGoebel May 28, 2026
8914928
Deserialization symbols
SusieGoebel May 28, 2026
e7c7743
desinging an extra internal API instead of symbols to reinstall IDE n…
SusieGoebel May 28, 2026
fbb4d12
Inverse triggers moved to public api object to keep IDE working
SusieGoebel May 28, 2026
5e38ef6
More methods just from Internal API of Referencable
SusieGoebel Jun 1, 2026
aa16b1d
Change API signature to expose Self and Parent as types, to tighten t…
SusieGoebel Jun 1, 2026
09c8c36
Move construction of refs from context initialization to ref's toJson
SusieGoebel Jun 1, 2026
7d52e21
Reorganize referencable methods
SusieGoebel Jun 1, 2026
caf0460
Reove explicit URI since we have the whole modelMeta currently available
SusieGoebel Jun 1, 2026
d3ae249
Make treechildren private, only expose access on internal API, hence …
SusieGoebel Jun 1, 2026
b8ba06e
Also make otherReferences private; on API as otherLinks, now only use…
SusieGoebel Jun 1, 2026
9a11c4d
private symbols for treeCHildren and links, now showing problem on tr…
SusieGoebel Jun 1, 2026
3d336d5
Precise typing to make sure that Referencable of P is kept as info
SusieGoebel Jun 1, 2026
3c96937
Apply same typing to tree interface
SusieGoebel Jun 1, 2026
a918fc4
Use P, not the T[Parent] for all wiring in order to keep the type exp…
SusieGoebel Jun 1, 2026
0de5ef9
parent containers on API but with less precise typing
SusieGoebel Jun 3, 2026
feb192d
Tight getter, still any on setter
SusieGoebel Jun 3, 2026
99fd011
Contravariance on set enforced via generic parameter on set
SusieGoebel Jun 3, 2026
19de716
remove container methods from public API
SusieGoebel Jun 3, 2026
bb426e9
Remove overwrite that has same behaviour as base version
SusieGoebel Jun 3, 2026
ab35493
rename user facing parent to getEParent to encode that it is the pare…
SusieGoebel Jun 3, 2026
bd2924c
hide internal paren container ref behind symbol
SusieGoebel Jun 3, 2026
337d778
use traversal to root to then strt assigning refs from there, instead…
SusieGoebel Jun 3, 2026
dbf708b
Make __referenceInitializers symbol hidden, as suggested by @Noah1891…
SusieGoebel Jun 5, 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
2 changes: 1 addition & 1 deletion projects/emfular/src/lib/binding/reference-creator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function createContainer<
}

if (meta.containment) {
const defaultEClass = parent.$modelUri + meta.target;
const defaultEClass = parent.$modelMeta.uri + meta.target;
if (isList) {
return new ReTreeListContainer<T>(parent, propertyKey, meta, defaultEClass )
} else {
Expand Down
8 changes: 4 additions & 4 deletions projects/emfular/src/lib/binding/reference-decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {createContainer} from "./reference-creator";
import {ReSingleInterface} from "../referencing/referencable/container/re-single-interface";
import {KindFromMeta, RefineReference} from "./reference-typing";
import {ReListInterface} from "../referencing/referencable/container/re-list-interface";
import {REFERENCE_INITIALIZERS} from "../referencing/referencable/referencable-symbols";

export function reference<T extends Referencable<any>, M extends ReferenceMeta>(
meta: M
Expand Down Expand Up @@ -65,11 +66,10 @@ export function reference<T extends Referencable<any>, M extends ReferenceMeta>(

}

if (!prototype.__referenceInitializers) {
prototype.__referenceInitializers = [];
if (!prototype[REFERENCE_INITIALIZERS]) {
prototype[REFERENCE_INITIALIZERS] = [];
}

(prototype.__referenceInitializers as Array<(this: any) => void>)
(prototype[REFERENCE_INITIALIZERS] as Array<(this: any) => void>)
.push(function (this: any) {
this[symbol] = createContainer<T, any>(
this, meta, String(propertyKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export function createListProxy<
const index = Number(prop);
const arr = container.get();
if (index < arr.length) {
arr[index].destruct();
arr[index].$destruct();
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 {REFERENCE_INTERNAL_API} from "../../referencable-symbols";

export class ReLinkListContainer<
T extends Referencable<any>,
Expand All @@ -15,14 +16,14 @@ implements ReLinkContainer<T, P> {

constructor(parent: P, name: string, refMeta: ReferenceMeta) {
super(parent, name, refMeta);
this._parent.$otherReferences.push(this)
this._parent[REFERENCE_INTERNAL_API].otherLinks().push(this)
}

addWithoutTypeCheck(item: T): boolean {
let res = ListUpdater.addToListIfMissing(item, this._instance)
if (res) {
if(this.inverseName !== undefined) {
return item.addToReferencableContainer(this.inverseName, this._parent)
return item[REFERENCE_INTERNAL_API].addToReference(this.inverseName, this._parent)
}
return true;
} else {
Expand All @@ -38,20 +39,16 @@ implements ReLinkContainer<T, P> {
const res = ListUpdater.removeFromList(item, this._instance)
if (res) {
if(this.inverseName !== undefined) {
item.removeFromReferencableContainer(this.inverseName, this._parent, mode)
item[REFERENCE_INTERNAL_API].removeFromReference(this.inverseName, this._parent, mode)
}
}
return res; //todo behaviour of flag different to add??
}

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

removeFromInverse(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean {
if(this.inverseName !== undefined) {
for (const child of [...this._instance]) {
child.removeFromReferencableContainer(this.inverseName, item, mode)
child[REFERENCE_INTERNAL_API].removeFromReference(this.inverseName, item, mode)
}
return true; // todo - refine?
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ReSingleChildExample2
} from "../../../test/re-containers-with-single-child";
import {DeletionMode} from "../../../../utils/deletion-mode";
import {REFERENCE_INTERNAL_API} from "../../referencable-symbols";

describe('ReLinkSingleContainer', () => {
it('should create an instance', () => {
Expand Down Expand Up @@ -41,8 +42,8 @@ describe('ReLinkSingleContainer', () => {
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(elem1[REFERENCE_INTERNAL_API].otherLinks()[0].remove(middle1)).toBeTrue();
expect(elem2[REFERENCE_INTERNAL_API].otherLinks()[0].remove(middle2)).toBeTrue();
expect(tester1.child).toBeDefined();
expect(tester1.child).toEqual(middle1);
expect(tester2.child).toBeDefined();
Expand Down Expand Up @@ -84,8 +85,8 @@ describe('ReLinkSingleContainer', () => {
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(elem1[REFERENCE_INTERNAL_API].otherLinks()[0].remove(middle1, DeletionMode.CASCADE)).toBeTrue();
expect(elem2[REFERENCE_INTERNAL_API].otherLinks()[0].remove(middle2, DeletionMode.CASCADE)).toBeTrue();
expect(tester1.child).toBeDefined();
expect(tester1.child).toEqual(middle1);
expect(tester2.child).toBeUndefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {ReLinkContainer} from "./re-link-container";
import {ReSingleContainer} from "../re-single-container";
import {ReferenceMeta} from "../../../../binding/model-definition";
import { DeletionMode } from "../../../../utils/deletion-mode";
import {REFERENCE_INTERNAL_API} from "../../referencable-symbols";

export class ReLinkSingleContainer<
T extends Referencable<any>,
Expand All @@ -14,14 +15,14 @@ implements ReLinkContainer<T, P> {

constructor(parent: P, referenceName: string, refMeta: ReferenceMeta) {
super(parent, referenceName, refMeta);
this._parent.$otherReferences.push(this)
this._parent[REFERENCE_INTERNAL_API].otherLinks().push(this)
}

protected set(instance: T): void {
if(this.inverseName !== undefined) {
this._instance?.removeFromReferencableContainer(this.inverseName, this._parent, DeletionMode.RELAXED)
this._instance?.[REFERENCE_INTERNAL_API].removeFromReference(this.inverseName, this._parent, DeletionMode.RELAXED)
this._instance = instance;
instance.addToReferencableContainer(this.inverseName, this._parent)
instance[REFERENCE_INTERNAL_API].addToReference(this.inverseName, this._parent)
} else {
this._instance = instance;
}
Expand All @@ -40,7 +41,7 @@ implements ReLinkContainer<T, P> {
if(this._instance == item) {
this._instance = undefined;
if (this.inverseName != undefined) {
item.removeFromReferencableContainer(this.inverseName, this._parent, mode)
item[REFERENCE_INTERNAL_API].removeFromReference(this.inverseName, this._parent, mode)
}
return true;
} else {
Expand All @@ -49,12 +50,12 @@ implements ReLinkContainer<T, P> {
}

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

removeFromInverse(item: T, mode: DeletionMode = DeletionMode.RELAXED): boolean {
if(this.inverseName !== undefined) {
this._instance?.removeFromReferencableContainer(this.inverseName, item, mode)
this._instance?.[REFERENCE_INTERNAL_API].removeFromReference(this.inverseName, item, mode)
return true; // todo refine?
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export abstract class ReContainer<

isAcceptableItem(item: Referencable<any>): boolean {
const expectedType = this.meta.target
const targetEclass = this._parent.$modelUri+expectedType //eclass composition only works since we work inside one model
const targetEclass = this._parent.$modelMeta.uri+expectedType //eclass composition only works since we work inside one model
let targetConstr = ModelRegistry.get(targetEclass)
return item instanceof targetConstr;
}
Expand All @@ -48,7 +48,7 @@ export abstract class ReContainer<

abstract remove(item: T, mode?: DeletionMode): boolean;

//called to destruct all elements in the container (e.g. when destroying a parent
//called to $destruct all elements in the container (e.g. when destroying a parent
abstract delete(mode?: DeletionMode): void

abstract toJson(ctx: SerializationContext): any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,30 @@ import {ReSingleInterface} from "../re-single-interface";
import {ReShallowInterface} from "./re-shallow-interface";
import {ReferenceMeta} from "../../../../binding/model-definition";
import { DeletionMode } from "../../../../utils/deletion-mode";
import {REFERENCE_INTERNAL_API} from "../../referencable-symbols";

export class ReTreeParentContainer<T extends Referencable<any>>
extends ReContainer<T["ParentType"],T>
implements ReSingleInterface<T["ParentType"], T>,
ReShallowInterface<T["ParentType"], T>{
export class ReTreeParentContainer<
T extends Referencable<any>,
P extends Referencable<T> = T["$ParentType"]>
extends ReContainer<P,T>
implements ReSingleInterface<P, T>,
ReShallowInterface<P, T>{

constructor(parent: T, referenceName: string, refMeta: ReferenceMeta) {
super(parent, referenceName, refMeta); // referenceName is actually unused for this container type
}

get(): T["ParentType"] | undefined {
return (this._parent.getParentReferencable() as T["ParentType"])
get(): P | undefined {
return (this._parent.$getEParent())
}

//todo rewrite without using item parent explicitly?
addWithoutTypeCheck(item: T["ParentType"]): boolean {
let me: T = this._parent
const currentParentCont = this._parent.parent
if(currentParentCont != undefined) {
currentParentCont.remove(this._parent as T["ParentType"], DeletionMode.RELAXED)
}
return item.addToReferencableContainer(this.inverseName, me)
addWithoutTypeCheck(item: P): boolean {
return item[REFERENCE_INTERNAL_API].addToReference(this.inverseName!, this._parent)
}

remove(item: T["ParentType"], mode: DeletionMode = DeletionMode.RELAXED): boolean {
return item.removeFromReferencableContainer(this.inverseName, this._parent, mode)
remove(item: P, mode: DeletionMode = DeletionMode.RELAXED): boolean {
return item[REFERENCE_INTERNAL_API].removeFromReference(this.inverseName!, this._parent, mode)
}

delete(): void {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { ReContainer } from "../re-container";
import {JsonOf} from "../../../../serialization/json-deserializable";

export interface ReTreeChildrenContainer<
T extends Referencable<any>,
> extends ReContainer<T, T["ParentType"]> {
T extends Referencable<P>,
P extends Referencable<any> =T["$ParentType"]
> extends ReContainer<T, P> {

// serialization
assignRefs(ctx: SerializationContext, path: string) : void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,26 @@ import {ListUpdater} from "../../../../utils/list-updater";
import {DeletionMode} from "../../../../utils/deletion-mode";
import {ReListContainer} from "../re-list-container";
import {ReferenceMeta} from "../../../../binding/model-definition";
import {REFERENCE_INTERNAL_API} from "../../referencable-symbols";

export class ReTreeListContainer<T extends Referencable<any>>
extends ReListContainer<T, T["ParentType"]>
export class ReTreeListContainer<
T extends Referencable<P>,
P extends Referencable<any> =T["$ParentType"]
> extends ReListContainer<T, P>
implements ReTreeChildrenContainer<T> {

readonly defaultEClass?: string;

constructor(parent: T["ParentType"], name: string, refMeta: ReferenceMeta, eClass?: string) {
constructor(parent: P, name: string, refMeta: ReferenceMeta, eClass?: string) {
super(parent, name, refMeta);
this.defaultEClass = eClass;
this._parent.$treeChildren.push(this)
this._parent[REFERENCE_INTERNAL_API].treeChildren().push(this)
}

assignRefs(ctx: SerializationContext, path: string) {
const ownPath = RefHandler.computePrefix(path, this.referenceName)
this._instance.map((elem, index) =>
elem.assignRefs(ctx, RefHandler.mixWithIndex(ownPath, index))
elem[REFERENCE_INTERNAL_API].serialize_assignRefs(ctx, RefHandler.mixWithIndex(ownPath, index))
)
}

Expand All @@ -36,11 +39,11 @@ implements ReTreeChildrenContainer<T> {

//todo rewrite without using item parent explicitly?
addWithoutTypeCheck(item: T): boolean {
const oldParent = item.parent;
const oldParent = item[REFERENCE_INTERNAL_API].getParentContainer();
if(oldParent == this) {
return false;
} else {
item.setParent(this);
item[REFERENCE_INTERNAL_API].setParentContainer(this);
oldParent?.remove(item)
return ListUpdater.addToListIfMissing(item, this._instance)
}
Expand All @@ -51,23 +54,19 @@ implements ReTreeChildrenContainer<T> {
if (this._instance.indexOf(item) > -1) {
// if remove is called on an items parent the CASCADE mode would cause an infinite loop,
// however this can be easily avoided since parent removal does not require any kind of following cascading deletes
item.destruct(mode);
item.$destruct(mode);
return true;
}
return false;
}
let removed = ListUpdater.removeFromList(item, this._instance)
if(removed){
item.setParent(undefined);
item[REFERENCE_INTERNAL_API].setParentContainer(undefined);
return true
}
return false;
}

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

//creates one child level plus calls next createChildren
fromJson(formerPrefix: string, context: Deserializer, json: any) {
let myJson: JsonOf<T>[] = json[this.referenceName];
Expand All @@ -91,7 +90,7 @@ implements ReTreeChildrenContainer<T> {
let myJson: JsonOf<T>[] = json[this.referenceName];
if(myJson && myJson.length == this._instance.length) {
myJson.forEach((ref, index) => {
this._instance[index].deserializeLinks(context, ref)
this._instance[index][REFERENCE_INTERNAL_API].deserializeOtherReferences(context, ref)
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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";
import {REFERENCE_INTERNAL_API} from "../../referencable-symbols";

describe('ReferencableTreeSingletonContainer', () => {
it('should create an instance', () => {
Expand All @@ -23,7 +24,7 @@ describe('ReferencableTreeSingletonContainer', () => {
expect(middle.otherLink).toEqual(elem1);
expect(elem1.link).toBeDefined();
expect(elem1.link).toEqual(middle);
expect(tester.$treeChildren[0].remove(middle)).toBeTrue();
expect(tester[REFERENCE_INTERNAL_API].treeChildren()[0].remove(middle)).toBeTrue();
expect(tester.child).toBeUndefined();
expect(middle.myParent).toBeUndefined();
expect(middle.otherLink).toBeDefined();
Expand All @@ -46,7 +47,7 @@ describe('ReferencableTreeSingletonContainer', () => {
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[REFERENCE_INTERNAL_API].treeChildren()[0].remove(middle, DeletionMode.CASCADE)).toBeTrue();
expect(tester.child).toBeUndefined();
expect(middle.myParent).toBeUndefined();
expect(middle.otherLink).toBeUndefined();
Expand Down
Loading